mirror of
https://github.com/VinylDNS/vinyldns
synced 2025-08-22 10:10:12 +00:00
Merge pull request #1348 from Jay07GIT/records_ownership_transfer
Ownership transfer for records
This commit is contained in:
commit
a3e848a52c
@ -19,6 +19,7 @@ package vinyldns.api.domain.record
|
|||||||
import cats.effect._
|
import cats.effect._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import cats.scalatest.EitherMatchers
|
import cats.scalatest.EitherMatchers
|
||||||
|
import org.mockito.Matchers.any
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import org.mockito.Mockito._
|
import org.mockito.Mockito._
|
||||||
@ -41,6 +42,9 @@ import vinyldns.core.domain.membership.{Group, GroupRepository, User, UserReposi
|
|||||||
import vinyldns.core.domain.record.RecordType._
|
import vinyldns.core.domain.record.RecordType._
|
||||||
import vinyldns.core.domain.record._
|
import vinyldns.core.domain.record._
|
||||||
import vinyldns.core.domain.zone._
|
import vinyldns.core.domain.zone._
|
||||||
|
import vinyldns.core.notifier.{AllNotifiers, Notification, Notifier}
|
||||||
|
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
|
||||||
class RecordSetServiceIntegrationSpec
|
class RecordSetServiceIntegrationSpec
|
||||||
extends AnyWordSpec
|
extends AnyWordSpec
|
||||||
@ -53,11 +57,16 @@ class RecordSetServiceIntegrationSpec
|
|||||||
with BeforeAndAfterAll
|
with BeforeAndAfterAll
|
||||||
with TransactionProvider {
|
with TransactionProvider {
|
||||||
|
|
||||||
|
private implicit val contextShift: ContextShift[IO] = IO.contextShift(ExecutionContext.global)
|
||||||
|
|
||||||
private val vinyldnsConfig = VinylDNSConfig.load().unsafeRunSync()
|
private val vinyldnsConfig = VinylDNSConfig.load().unsafeRunSync()
|
||||||
|
|
||||||
private val recordSetRepo = recordSetRepository
|
private val recordSetRepo = recordSetRepository
|
||||||
private val recordSetCacheRepo = recordSetCacheRepository
|
private val recordSetCacheRepo = recordSetCacheRepository
|
||||||
|
|
||||||
|
private val mockNotifier = mock[Notifier]
|
||||||
|
private val mockNotifiers = AllNotifiers(List(mockNotifier))
|
||||||
|
|
||||||
private val zoneRepo: ZoneRepository = zoneRepository
|
private val zoneRepo: ZoneRepository = zoneRepository
|
||||||
private val groupRepo: GroupRepository = groupRepository
|
private val groupRepo: GroupRepository = groupRepository
|
||||||
|
|
||||||
@ -232,6 +241,36 @@ class RecordSetServiceIntegrationSpec
|
|||||||
ownerGroupId = Some("non-existent")
|
ownerGroupId = Some("non-existent")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val sharedTestRecordPendingReviewOwnerShip = RecordSet(
|
||||||
|
sharedZone.id,
|
||||||
|
"shared-record-ownerShip-pendingReview",
|
||||||
|
A,
|
||||||
|
200,
|
||||||
|
RecordSetStatus.Active,
|
||||||
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
|
None,
|
||||||
|
List(AData("1.1.1.1")),
|
||||||
|
ownerGroupId = Some(sharedGroup.id),
|
||||||
|
recordSetGroupChange = Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.PendingReview,
|
||||||
|
requestedOwnerGroupId = Some(group.id)))
|
||||||
|
)
|
||||||
|
|
||||||
|
private val sharedTestRecordCancelledOwnerShip = RecordSet(
|
||||||
|
sharedZone.id,
|
||||||
|
"shared-record-ownerShip-cancelled",
|
||||||
|
A,
|
||||||
|
200,
|
||||||
|
RecordSetStatus.Active,
|
||||||
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
|
None,
|
||||||
|
List(AData("1.1.1.1")),
|
||||||
|
ownerGroupId = Some(sharedGroup.id),
|
||||||
|
recordSetGroupChange = Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.Cancelled,
|
||||||
|
requestedOwnerGroupId = Some(group.id)))
|
||||||
|
)
|
||||||
|
|
||||||
private val testOwnerGroupRecordInNormalZone = RecordSet(
|
private val testOwnerGroupRecordInNormalZone = RecordSet(
|
||||||
zone.id,
|
zone.id,
|
||||||
"user-in-owner-group-but-zone-not-shared",
|
"user-in-owner-group-but-zone-not-shared",
|
||||||
@ -285,7 +324,10 @@ class RecordSetServiceIntegrationSpec
|
|||||||
// Seeding records in DB
|
// Seeding records in DB
|
||||||
val sharedRecords = List(
|
val sharedRecords = List(
|
||||||
sharedTestRecord,
|
sharedTestRecord,
|
||||||
sharedTestRecordBadOwnerGroup
|
sharedTestRecordBadOwnerGroup,
|
||||||
|
sharedTestRecordPendingReviewOwnerShip,
|
||||||
|
sharedTestRecordCancelledOwnerShip
|
||||||
|
|
||||||
)
|
)
|
||||||
val conflictRecords = List(
|
val conflictRecords = List(
|
||||||
subTestRecordNameConflict,
|
subTestRecordNameConflict,
|
||||||
@ -324,7 +366,8 @@ class RecordSetServiceIntegrationSpec
|
|||||||
vinyldnsConfig.highValueDomainConfig,
|
vinyldnsConfig.highValueDomainConfig,
|
||||||
vinyldnsConfig.dottedHostsConfig,
|
vinyldnsConfig.dottedHostsConfig,
|
||||||
vinyldnsConfig.serverConfig.approvedNameServers,
|
vinyldnsConfig.serverConfig.approvedNameServers,
|
||||||
useRecordSetCache = true
|
useRecordSetCache = true,
|
||||||
|
mockNotifiers
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,6 +468,227 @@ class RecordSetServiceIntegrationSpec
|
|||||||
leftValue(result) shouldBe a[InvalidRequest]
|
leftValue(result) shouldBe a[InvalidRequest]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"auto-approve ownership transfer request, if user tried to update the ownership" in {
|
||||||
|
val newRecord = sharedTestRecord.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(ownerShipTransferStatus = OwnerShipTransferStatus.AutoApproved,
|
||||||
|
requestedOwnerGroupId = Some(group.id))))
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, auth2)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
val change = rightValue(result).asInstanceOf[RecordSetChange]
|
||||||
|
change.recordSet.name shouldBe "shared-record"
|
||||||
|
change.recordSet.ownerGroupId.get shouldBe group.id
|
||||||
|
change.recordSet.recordSetGroupChange.get.ownerShipTransferStatus shouldBe OwnerShipTransferStatus.AutoApproved
|
||||||
|
change.recordSet.recordSetGroupChange.get.requestedOwnerGroupId.get shouldBe group.id
|
||||||
|
}
|
||||||
|
|
||||||
|
"approve ownership transfer request, if user requested for ownership transfer" in {
|
||||||
|
val newRecord = sharedTestRecordPendingReviewOwnerShip.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyApproved)))
|
||||||
|
|
||||||
|
doReturn(IO.unit).when(mockNotifier).notify(any[Notification[_]])
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, auth2)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
val change = rightValue(result).asInstanceOf[RecordSetChange]
|
||||||
|
change.recordSet.name shouldBe "shared-record-ownerShip-pendingReview"
|
||||||
|
change.recordSet.ownerGroupId.get shouldBe group.id
|
||||||
|
change.recordSet.recordSetGroupChange.get.ownerShipTransferStatus shouldBe OwnerShipTransferStatus.ManuallyApproved
|
||||||
|
change.recordSet.recordSetGroupChange.get.requestedOwnerGroupId.get shouldBe group.id
|
||||||
|
}
|
||||||
|
|
||||||
|
"reject ownership transfer request, if user requested for ownership transfer" in {
|
||||||
|
val newRecord = sharedTestRecordPendingReviewOwnerShip.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyRejected)))
|
||||||
|
|
||||||
|
doReturn(IO.unit).when(mockNotifier).notify(any[Notification[_]])
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, auth2)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
val change = rightValue(result).asInstanceOf[RecordSetChange]
|
||||||
|
change.recordSet.name shouldBe "shared-record-ownerShip-pendingReview"
|
||||||
|
change.recordSet.ownerGroupId.get shouldBe sharedGroup.id
|
||||||
|
change.recordSet.recordSetGroupChange.get.ownerShipTransferStatus shouldBe OwnerShipTransferStatus.ManuallyRejected
|
||||||
|
change.recordSet.recordSetGroupChange.get.requestedOwnerGroupId.get shouldBe group.id
|
||||||
|
}
|
||||||
|
|
||||||
|
"request ownership transfer, if user not in the owner group and wants to own the record" in {
|
||||||
|
val newRecord = sharedTestRecord.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.Requested,
|
||||||
|
requestedOwnerGroupId = Some(dummyGroup.id))))
|
||||||
|
|
||||||
|
doReturn(IO.unit).when(mockNotifier).notify(any[Notification[_]])
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, dummyAuth)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
val change = rightValue(result).asInstanceOf[RecordSetChange]
|
||||||
|
change.recordSet.name shouldBe "shared-record"
|
||||||
|
change.recordSet.ownerGroupId.get shouldBe sharedGroup.id
|
||||||
|
change.recordSet.recordSetGroupChange.get.ownerShipTransferStatus shouldBe OwnerShipTransferStatus.PendingReview
|
||||||
|
change.recordSet.recordSetGroupChange.get.requestedOwnerGroupId.get shouldBe dummyGroup.id
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail requesting ownership transfer if user is not in owner group and tried to update other fields in record set" in {
|
||||||
|
val newRecord = sharedTestRecord.copy(
|
||||||
|
ttl = 3000,
|
||||||
|
recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.Requested,
|
||||||
|
requestedOwnerGroupId = Some(dummyGroup.id))))
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, dummyAuth)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
leftValue(result) shouldBe a[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail updating if user is not in owner group for ownership transfer approval" in {
|
||||||
|
val newRecord = sharedTestRecordPendingReviewOwnerShip.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyApproved)))
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, dummyAuth)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
leftValue(result) shouldBe a[NotAuthorizedError]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail updating if user is not in owner group for ownership transfer reject" in {
|
||||||
|
val newRecord = sharedTestRecordPendingReviewOwnerShip.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyRejected)))
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, dummyAuth)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
leftValue(result) shouldBe a[NotAuthorizedError]
|
||||||
|
}
|
||||||
|
|
||||||
|
"cancel the ownership transfer request, if user not require ownership transfer further" in {
|
||||||
|
val newRecord = sharedTestRecordPendingReviewOwnerShip.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.Cancelled)))
|
||||||
|
|
||||||
|
doReturn(IO.unit).when(mockNotifier).notify(any[Notification[_]])
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, auth)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
val change = rightValue(result).asInstanceOf[RecordSetChange]
|
||||||
|
change.recordSet.name shouldBe "shared-record-ownerShip-pendingReview"
|
||||||
|
change.recordSet.ownerGroupId.get shouldBe sharedGroup.id
|
||||||
|
change.recordSet.recordSetGroupChange.get.ownerShipTransferStatus shouldBe OwnerShipTransferStatus.Cancelled
|
||||||
|
change.recordSet.recordSetGroupChange.get.requestedOwnerGroupId.get shouldBe group.id
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail approving ownership transfer request, if user is cancelled" in {
|
||||||
|
val newRecord = sharedTestRecordCancelledOwnerShip.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyApproved)))
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, auth)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
leftValue(result) shouldBe a[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail rejecting ownership transfer request, if user is cancelled" in {
|
||||||
|
val newRecord = sharedTestRecordCancelledOwnerShip.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyRejected)))
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, auth)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
leftValue(result) shouldBe a[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail auto-approving ownership transfer request, if user is cancelled" in {
|
||||||
|
val newRecord = sharedTestRecordCancelledOwnerShip.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.AutoApproved
|
||||||
|
)))
|
||||||
|
|
||||||
|
doReturn(IO.unit).when(mockNotifier).notify(any[Notification[_]])
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, auth)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
leftValue(result) shouldBe a[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail auto-approving ownership transfer request, if zone is not shared" in {
|
||||||
|
val newRecord = dottedTestRecord.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(ownerShipTransferStatus = OwnerShipTransferStatus.AutoApproved,
|
||||||
|
requestedOwnerGroupId = Some(group.id))))
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, auth2)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
leftValue(result) shouldBe a[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail approving ownership transfer request, if zone is not shared" in {
|
||||||
|
val newRecord = dottedTestRecord.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyApproved
|
||||||
|
)))
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, auth2)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
leftValue(result) shouldBe a[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail requesting ownership transfer, if zone is not shared" in {
|
||||||
|
val newRecord = dottedTestRecord.copy(recordSetGroupChange =
|
||||||
|
Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.Requested,
|
||||||
|
requestedOwnerGroupId = Some(dummyGroup.id)
|
||||||
|
)))
|
||||||
|
|
||||||
|
doReturn(IO.unit).when(mockNotifier).notify(any[Notification[_]])
|
||||||
|
|
||||||
|
val result = testRecordSetService
|
||||||
|
.updateRecordSet(newRecord, dummyAuth)
|
||||||
|
.value
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
leftValue(result) shouldBe a[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
"update dotted record succeeds if it satisfies all dotted hosts config" in {
|
"update dotted record succeeds if it satisfies all dotted hosts config" in {
|
||||||
val newRecord = dottedTestRecord.copy(ttl = 37000)
|
val newRecord = dottedTestRecord.copy(ttl = 37000)
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ import org.scalatest.matchers.should.Matchers
|
|||||||
import org.scalatest.wordspec.AnyWordSpecLike
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
import vinyldns.core.domain.batch._
|
import vinyldns.core.domain.batch._
|
||||||
import vinyldns.core.domain.record.RecordType
|
import vinyldns.core.domain.record.RecordType
|
||||||
import vinyldns.core.domain.record.AData
|
import vinyldns.core.domain.record.{AData, OwnerShipTransferStatus, RecordSetChange, RecordSetChangeStatus, RecordSetChangeType, RecordType}
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import vinyldns.core.TestMembershipData._
|
import vinyldns.core.TestMembershipData._
|
||||||
@ -35,6 +35,8 @@ import cats.effect.{IO, Resource}
|
|||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import org.scalatest.BeforeAndAfterEach
|
import org.scalatest.BeforeAndAfterEach
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
import vinyldns.core.TestRecordSetData.{ownerShipTransfer, rsOk}
|
||||||
|
import vinyldns.core.TestZoneData.okZone
|
||||||
|
|
||||||
class EmailNotifierIntegrationSpec
|
class EmailNotifierIntegrationSpec
|
||||||
extends MySqlApiIntegrationSpec
|
extends MySqlApiIntegrationSpec
|
||||||
@ -57,7 +59,7 @@ class EmailNotifierIntegrationSpec
|
|||||||
|
|
||||||
"Email Notifier" should {
|
"Email Notifier" should {
|
||||||
|
|
||||||
"send an email" taggedAs (SkipCI) in {
|
"send an email for batch change" taggedAs (SkipCI) in {
|
||||||
val batchChange = BatchChange(
|
val batchChange = BatchChange(
|
||||||
okUser.id,
|
okUser.id,
|
||||||
okUser.userName,
|
okUser.userName,
|
||||||
@ -84,7 +86,7 @@ class EmailNotifierIntegrationSpec
|
|||||||
val program = for {
|
val program = for {
|
||||||
_ <- userRepository.save(okUser)
|
_ <- userRepository.save(okUser)
|
||||||
notifier <- new EmailNotifierProvider()
|
notifier <- new EmailNotifierProvider()
|
||||||
.load(NotifierConfig("", emailConfig), userRepository)
|
.load(NotifierConfig("", emailConfig), userRepository, groupRepository)
|
||||||
_ <- notifier.notify(Notification(batchChange))
|
_ <- notifier.notify(Notification(batchChange))
|
||||||
emailFiles <- retrieveEmailFiles(targetDirectory)
|
emailFiles <- retrieveEmailFiles(targetDirectory)
|
||||||
} yield emailFiles
|
} yield emailFiles
|
||||||
@ -94,7 +96,29 @@ class EmailNotifierIntegrationSpec
|
|||||||
files.length should be(1)
|
files.length should be(1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
"send an email for recordSetChange ownerShip transfer" taggedAs (SkipCI) in {
|
||||||
|
val recordSetChange = RecordSetChange(
|
||||||
|
okZone,
|
||||||
|
rsOk.copy(ownerGroupId= Some(okGroup.id),recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.PendingReview, requestedOwnerGroupId = Some(dummyGroup.id)))),
|
||||||
|
"system",
|
||||||
|
RecordSetChangeType.Create,
|
||||||
|
RecordSetChangeStatus.Complete
|
||||||
|
)
|
||||||
|
|
||||||
|
val program = for {
|
||||||
|
_ <- userRepository.save(okUser)
|
||||||
|
notifier <- new EmailNotifierProvider()
|
||||||
|
.load(NotifierConfig("", emailConfig), userRepository, groupRepository)
|
||||||
|
_ <- notifier.notify(Notification(recordSetChange))
|
||||||
|
emailFiles <- retrieveEmailFiles(targetDirectory)
|
||||||
|
} yield emailFiles
|
||||||
|
|
||||||
|
val files = program.unsafeRunSync()
|
||||||
|
|
||||||
|
files.length should be(1)
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteEmailFiles(path: Path): IO[Unit] =
|
def deleteEmailFiles(path: Path): IO[Unit] =
|
||||||
|
@ -111,7 +111,7 @@ class SnsNotifierIntegrationSpec
|
|||||||
sns.subscribe(topic, "sqs", queueUrl)
|
sns.subscribe(topic, "sqs", queueUrl)
|
||||||
}
|
}
|
||||||
notifier <- new SnsNotifierProvider()
|
notifier <- new SnsNotifierProvider()
|
||||||
.load(NotifierConfig("", snsConfig), userRepository)
|
.load(NotifierConfig("", snsConfig), userRepository, groupRepository)
|
||||||
_ <- notifier.notify(Notification(batchChange))
|
_ <- notifier.notify(Notification(batchChange))
|
||||||
_ <- IO.sleep(1.seconds)
|
_ <- IO.sleep(1.seconds)
|
||||||
messages <- IO {
|
messages <- IO {
|
||||||
|
@ -96,7 +96,8 @@ object Boot extends App {
|
|||||||
msgsPerPoll <- IO.fromEither(MessageCount(vinyldnsConfig.messageQueueConfig.messagesPerPoll))
|
msgsPerPoll <- IO.fromEither(MessageCount(vinyldnsConfig.messageQueueConfig.messagesPerPoll))
|
||||||
notifiers <- NotifierLoader.loadAll(
|
notifiers <- NotifierLoader.loadAll(
|
||||||
vinyldnsConfig.notifierConfigs,
|
vinyldnsConfig.notifierConfigs,
|
||||||
repositories.userRepository
|
repositories.userRepository,
|
||||||
|
repositories.groupRepository
|
||||||
)
|
)
|
||||||
_ <- APIMetrics.initialize(vinyldnsConfig.apiMetricSettings)
|
_ <- APIMetrics.initialize(vinyldnsConfig.apiMetricSettings)
|
||||||
// Schedule the zone sync task to be executed every 5 seconds
|
// Schedule the zone sync task to be executed every 5 seconds
|
||||||
@ -161,7 +162,8 @@ object Boot extends App {
|
|||||||
vinyldnsConfig.highValueDomainConfig,
|
vinyldnsConfig.highValueDomainConfig,
|
||||||
vinyldnsConfig.dottedHostsConfig,
|
vinyldnsConfig.dottedHostsConfig,
|
||||||
vinyldnsConfig.serverConfig.approvedNameServers,
|
vinyldnsConfig.serverConfig.approvedNameServers,
|
||||||
vinyldnsConfig.serverConfig.useRecordSetCache
|
vinyldnsConfig.serverConfig.useRecordSetCache,
|
||||||
|
notifiers
|
||||||
)
|
)
|
||||||
val zoneService = ZoneService(
|
val zoneService = ZoneService(
|
||||||
repositories,
|
repositories,
|
||||||
|
@ -205,7 +205,8 @@ trait DnsConversions {
|
|||||||
ttl = r.getTTL,
|
ttl = r.getTTL,
|
||||||
status = RecordSetStatus.Active,
|
status = RecordSetStatus.Active,
|
||||||
created = Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
created = Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
records = f(r)
|
records = f(r),
|
||||||
|
recordSetGroupChange = Some(OwnerShipTransfer(ownerShipTransferStatus = OwnerShipTransferStatus.None))
|
||||||
)
|
)
|
||||||
|
|
||||||
// if we do not know the record type, then we cannot parse the records, but we should be able to get everything else
|
// if we do not know the record type, then we cannot parse the records, but we should be able to get everything else
|
||||||
@ -217,7 +218,8 @@ trait DnsConversions {
|
|||||||
ttl = r.getTTL,
|
ttl = r.getTTL,
|
||||||
status = RecordSetStatus.Active,
|
status = RecordSetStatus.Active,
|
||||||
created = Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
created = Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
records = Nil
|
records = Nil,
|
||||||
|
recordSetGroupChange = Some(OwnerShipTransfer(ownerShipTransferStatus = OwnerShipTransferStatus.None))
|
||||||
)
|
)
|
||||||
|
|
||||||
def fromARecord(r: DNS.ARecord, zoneName: DNS.Name, zoneId: String): RecordSet =
|
def fromARecord(r: DNS.ARecord, zoneName: DNS.Name, zoneId: String): RecordSet =
|
||||||
|
@ -240,7 +240,8 @@ class BatchChangeConverter(batchChangeRepo: BatchChangeRepository, messageQueue:
|
|||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
proposedRecordData.toList,
|
proposedRecordData.toList,
|
||||||
ownerGroupId = setOwnerGroupId
|
ownerGroupId = setOwnerGroupId,
|
||||||
|
recordSetGroupChange = Some(OwnerShipTransfer(ownerShipTransferStatus = OwnerShipTransferStatus.None))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ import vinyldns.core.domain.record.RecordType.RecordType
|
|||||||
import vinyldns.core.domain.DomainHelpers.ensureTrailingDot
|
import vinyldns.core.domain.DomainHelpers.ensureTrailingDot
|
||||||
import vinyldns.core.domain.backend.{Backend, BackendResolver}
|
import vinyldns.core.domain.backend.{Backend, BackendResolver}
|
||||||
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
|
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
|
||||||
|
import vinyldns.core.notifier.{AllNotifiers, Notification}
|
||||||
|
|
||||||
import scala.util.matching.Regex
|
import scala.util.matching.Regex
|
||||||
|
|
||||||
@ -49,7 +50,8 @@ object RecordSetService {
|
|||||||
highValueDomainConfig: HighValueDomainConfig,
|
highValueDomainConfig: HighValueDomainConfig,
|
||||||
dottedHostsConfig: DottedHostsConfig,
|
dottedHostsConfig: DottedHostsConfig,
|
||||||
approvedNameServers: List[Regex],
|
approvedNameServers: List[Regex],
|
||||||
useRecordSetCache: Boolean
|
useRecordSetCache: Boolean,
|
||||||
|
notifiers: AllNotifiers
|
||||||
): RecordSetService =
|
): RecordSetService =
|
||||||
new RecordSetService(
|
new RecordSetService(
|
||||||
dataAccessor.zoneRepository,
|
dataAccessor.zoneRepository,
|
||||||
@ -65,7 +67,9 @@ object RecordSetService {
|
|||||||
highValueDomainConfig,
|
highValueDomainConfig,
|
||||||
dottedHostsConfig,
|
dottedHostsConfig,
|
||||||
approvedNameServers,
|
approvedNameServers,
|
||||||
useRecordSetCache
|
useRecordSetCache,
|
||||||
|
notifiers
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,12 +87,16 @@ class RecordSetService(
|
|||||||
highValueDomainConfig: HighValueDomainConfig,
|
highValueDomainConfig: HighValueDomainConfig,
|
||||||
dottedHostsConfig: DottedHostsConfig,
|
dottedHostsConfig: DottedHostsConfig,
|
||||||
approvedNameServers: List[Regex],
|
approvedNameServers: List[Regex],
|
||||||
useRecordSetCache: Boolean
|
useRecordSetCache: Boolean,
|
||||||
|
notifiers: AllNotifiers
|
||||||
) extends RecordSetServiceAlgebra {
|
) extends RecordSetServiceAlgebra {
|
||||||
|
|
||||||
import RecordSetValidations._
|
import RecordSetValidations._
|
||||||
import accessValidation._
|
import accessValidation._
|
||||||
|
|
||||||
|
val approverOwnerShipTransferStatus = List(OwnerShipTransferStatus.ManuallyApproved , OwnerShipTransferStatus.AutoApproved, OwnerShipTransferStatus.ManuallyRejected)
|
||||||
|
val requestorOwnerShipTransferStatus = List(OwnerShipTransferStatus.Cancelled , OwnerShipTransferStatus.Requested, OwnerShipTransferStatus.PendingReview)
|
||||||
|
|
||||||
def addRecordSet(recordSet: RecordSet, auth: AuthPrincipal): Result[ZoneCommandResult] =
|
def addRecordSet(recordSet: RecordSet, auth: AuthPrincipal): Result[ZoneCommandResult] =
|
||||||
for {
|
for {
|
||||||
zone <- getZone(recordSet.zoneId)
|
zone <- getZone(recordSet.zoneId)
|
||||||
@ -143,14 +151,28 @@ class RecordSetService(
|
|||||||
_ <- unchangedRecordName(existing, recordSet, zone).toResult
|
_ <- unchangedRecordName(existing, recordSet, zone).toResult
|
||||||
_ <- unchangedRecordType(existing, recordSet).toResult
|
_ <- unchangedRecordType(existing, recordSet).toResult
|
||||||
_ <- unchangedZoneId(existing, recordSet).toResult
|
_ <- unchangedZoneId(existing, recordSet).toResult
|
||||||
|
_ <- if(requestorOwnerShipTransferStatus.contains(recordSet.recordSetGroupChange.map(_.ownerShipTransferStatus).getOrElse("<none>"))
|
||||||
|
&& !auth.isSuper && !auth.isGroupMember(existing.ownerGroupId.getOrElse("None")))
|
||||||
|
unchangedRecordSet(existing, recordSet).toResult else ().toResult
|
||||||
|
_ <- if(existing.recordSetGroupChange.map(_.ownerShipTransferStatus).getOrElse("<none>") == OwnerShipTransferStatus.Cancelled
|
||||||
|
&& !auth.isSuper)
|
||||||
|
recordSetOwnerShipApproveStatus(recordSet).toResult else ().toResult
|
||||||
|
recordSet <- updateRecordSetGroupChangeStatus(recordSet, existing, zone)
|
||||||
change <- RecordSetChangeGenerator.forUpdate(existing, recordSet, zone, Some(auth)).toResult
|
change <- RecordSetChangeGenerator.forUpdate(existing, recordSet, zone, Some(auth)).toResult
|
||||||
// because changes happen to the RS in forUpdate itself, converting 1st and validating on that
|
// because changes happen to the RS in forUpdate itself, converting 1st and validating on that
|
||||||
rsForValidations = change.recordSet
|
rsForValidations = change.recordSet
|
||||||
superUserCanUpdateOwnerGroup = canSuperUserUpdateOwnerGroup(existing, recordSet, zone, auth)
|
superUserCanUpdateOwnerGroup = canSuperUserUpdateOwnerGroup(existing, recordSet, zone, auth)
|
||||||
_ <- isNotHighValueDomain(recordSet, zone, highValueDomainConfig).toResult
|
_ <- isNotHighValueDomain(recordSet, zone, highValueDomainConfig).toResult
|
||||||
_ <- canUpdateRecordSet(auth, existing.name, existing.typ, zone, existing.ownerGroupId, superUserCanUpdateOwnerGroup).toResult
|
_ <- if(requestorOwnerShipTransferStatus.contains(recordSet.recordSetGroupChange.map(_.ownerShipTransferStatus).getOrElse("<none>"))
|
||||||
|
&& !auth.isSuper && !auth.isGroupMember(existing.ownerGroupId.getOrElse("None"))) ().toResult
|
||||||
|
else canUpdateRecordSet(auth, existing.name, existing.typ, zone, existing.ownerGroupId, superUserCanUpdateOwnerGroup).toResult
|
||||||
ownerGroup <- getGroupIfProvided(rsForValidations.ownerGroupId)
|
ownerGroup <- getGroupIfProvided(rsForValidations.ownerGroupId)
|
||||||
_ <- canUseOwnerGroup(rsForValidations.ownerGroupId, ownerGroup, auth).toResult
|
_ <- if(requestorOwnerShipTransferStatus.contains(recordSet.recordSetGroupChange.map(_.ownerShipTransferStatus).getOrElse("<none>"))
|
||||||
|
&& !auth.isSuper && !auth.isGroupMember(existing.ownerGroupId.getOrElse("None")))
|
||||||
|
canUseOwnerGroup(rsForValidations.recordSetGroupChange.map(_.requestedOwnerGroupId).get, ownerGroup, auth).toResult
|
||||||
|
else if(approverOwnerShipTransferStatus.contains(recordSet.recordSetGroupChange.map(_.ownerShipTransferStatus).getOrElse("<none>"))
|
||||||
|
&& !auth.isSuper) canUseOwnerGroup(existing.ownerGroupId, ownerGroup, auth).toResult
|
||||||
|
else canUseOwnerGroup(rsForValidations.ownerGroupId, ownerGroup, auth).toResult
|
||||||
_ <- notPending(existing).toResult
|
_ <- notPending(existing).toResult
|
||||||
existingRecordsWithName <- recordSetRepository
|
existingRecordsWithName <- recordSetRepository
|
||||||
.getRecordSetsByName(zone.id, rsForValidations.name)
|
.getRecordSetsByName(zone.id, rsForValidations.name)
|
||||||
@ -185,6 +207,11 @@ class RecordSetService(
|
|||||||
_ <- if(existing.name == rsForValidations.name) ().toResult else if(allowedZoneList.contains(zone.name)) checkAllowedDots(allowedDotsLimit, rsForValidations, zone).toResult else ().toResult
|
_ <- if(existing.name == rsForValidations.name) ().toResult else if(allowedZoneList.contains(zone.name)) checkAllowedDots(allowedDotsLimit, rsForValidations, zone).toResult else ().toResult
|
||||||
_ <- if(allowedZoneList.contains(zone.name)) isNotApexEndsWithDot(rsForValidations, zone).toResult else ().toResult
|
_ <- if(allowedZoneList.contains(zone.name)) isNotApexEndsWithDot(rsForValidations, zone).toResult else ().toResult
|
||||||
_ <- messageQueue.send(change).toResult[Unit]
|
_ <- messageQueue.send(change).toResult[Unit]
|
||||||
|
_ <- if(recordSet.recordSetGroupChange != None &&
|
||||||
|
recordSet.recordSetGroupChange.map(_.ownerShipTransferStatus).getOrElse("<none>") != OwnerShipTransferStatus.None &&
|
||||||
|
recordSet.recordSetGroupChange.map(_.ownerShipTransferStatus).getOrElse("<none>") != OwnerShipTransferStatus.AutoApproved)
|
||||||
|
notifiers.notify(Notification(change)).toResult
|
||||||
|
else ().toResult
|
||||||
} yield change
|
} yield change
|
||||||
|
|
||||||
def deleteRecordSet(
|
def deleteRecordSet(
|
||||||
@ -203,6 +230,65 @@ class RecordSetService(
|
|||||||
_ <- messageQueue.send(change).toResult[Unit]
|
_ <- messageQueue.send(change).toResult[Unit]
|
||||||
} yield change
|
} yield change
|
||||||
|
|
||||||
|
//update ownership transfer is zone is shared
|
||||||
|
def updateRecordSetGroupChangeStatus(recordSet: RecordSet, existing: RecordSet, zone: Zone): Result[RecordSet] = {
|
||||||
|
val existingOwnerShipTransfer = existing.recordSetGroupChange.getOrElse(OwnerShipTransfer.apply(OwnerShipTransferStatus.None, Some("none")))
|
||||||
|
val ownerShipTransfer = recordSet.recordSetGroupChange.getOrElse(OwnerShipTransfer.apply(OwnerShipTransferStatus.None, Some("none")))
|
||||||
|
if (recordSet.recordSetGroupChange != None &&
|
||||||
|
ownerShipTransfer.ownerShipTransferStatus != OwnerShipTransferStatus.None)
|
||||||
|
if (zone.shared){
|
||||||
|
if (approverOwnerShipTransferStatus.contains(ownerShipTransfer.ownerShipTransferStatus)) {
|
||||||
|
val recordSetOwnerApproval =
|
||||||
|
ownerShipTransfer.ownerShipTransferStatus match {
|
||||||
|
case OwnerShipTransferStatus.ManuallyApproved =>
|
||||||
|
recordSet.copy(ownerGroupId = existingOwnerShipTransfer.requestedOwnerGroupId,
|
||||||
|
recordSetGroupChange = Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyApproved,
|
||||||
|
requestedOwnerGroupId = existingOwnerShipTransfer.requestedOwnerGroupId)))
|
||||||
|
case OwnerShipTransferStatus.ManuallyRejected =>
|
||||||
|
recordSet.copy(
|
||||||
|
recordSetGroupChange = Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyRejected,
|
||||||
|
requestedOwnerGroupId = existingOwnerShipTransfer.requestedOwnerGroupId)))
|
||||||
|
case OwnerShipTransferStatus.AutoApproved =>
|
||||||
|
recordSet.copy(
|
||||||
|
ownerGroupId = ownerShipTransfer.requestedOwnerGroupId,
|
||||||
|
recordSetGroupChange = Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.AutoApproved,
|
||||||
|
requestedOwnerGroupId = ownerShipTransfer.requestedOwnerGroupId)))
|
||||||
|
|
||||||
|
case _ => recordSet.copy(
|
||||||
|
recordSetGroupChange = Some(ownerShipTransfer.copy(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.None,
|
||||||
|
requestedOwnerGroupId = Some("null"))))
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
recordSet <- recordSetOwnerApproval.toResult
|
||||||
|
} yield recordSet
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val recordSetOwnerRequest =
|
||||||
|
ownerShipTransfer.ownerShipTransferStatus match {
|
||||||
|
case OwnerShipTransferStatus.Cancelled =>
|
||||||
|
recordSet.copy(recordSetGroupChange = Some(ownerShipTransfer.copy(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.Cancelled,
|
||||||
|
requestedOwnerGroupId = existingOwnerShipTransfer.requestedOwnerGroupId)))
|
||||||
|
case OwnerShipTransferStatus.Requested | OwnerShipTransferStatus.PendingReview => recordSet.copy(
|
||||||
|
recordSetGroupChange = Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.PendingReview)))
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
recordSet <- recordSetOwnerRequest.toResult
|
||||||
|
} yield recordSet
|
||||||
|
}
|
||||||
|
} else for {
|
||||||
|
_ <- unchangedRecordSetOwnershipStatus(recordSet, existing).toResult
|
||||||
|
} yield recordSet.copy(
|
||||||
|
recordSetGroupChange = Some(ownerShipTransfer.copy(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.None,
|
||||||
|
requestedOwnerGroupId = Some("null"))))
|
||||||
|
else recordSet.copy(
|
||||||
|
recordSetGroupChange = Some(ownerShipTransfer.copy(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.None,
|
||||||
|
requestedOwnerGroupId = Some("null")))).toResult
|
||||||
|
}
|
||||||
|
|
||||||
// For dotted hosts. Check if a record that may conflict with dotted host exist or not
|
// For dotted hosts. Check if a record that may conflict with dotted host exist or not
|
||||||
def recordFQDNDoesNotExist(newRecordSet: RecordSet, zone: Zone): IO[Boolean] = {
|
def recordFQDNDoesNotExist(newRecordSet: RecordSet, zone: Zone): IO[Boolean] = {
|
||||||
// Use fqdn for searching through `recordset` mysql table to see if it already exist
|
// Use fqdn for searching through `recordset` mysql table to see if it already exist
|
||||||
|
@ -27,7 +27,7 @@ import vinyldns.core.domain.record.RecordType._
|
|||||||
import vinyldns.api.domain.zone._
|
import vinyldns.api.domain.zone._
|
||||||
import vinyldns.core.domain.auth.AuthPrincipal
|
import vinyldns.core.domain.auth.AuthPrincipal
|
||||||
import vinyldns.core.domain.membership.Group
|
import vinyldns.core.domain.membership.Group
|
||||||
import vinyldns.core.domain.record.{RecordSet, RecordType}
|
import vinyldns.core.domain.record.{OwnerShipTransferStatus, RecordSet, RecordType}
|
||||||
import vinyldns.core.domain.zone.Zone
|
import vinyldns.core.domain.zone.Zone
|
||||||
import vinyldns.core.Messages._
|
import vinyldns.core.Messages._
|
||||||
|
|
||||||
@ -462,4 +462,41 @@ object RecordSetValidations {
|
|||||||
val wildcardRegex = raw"^\s*[*%].*[*%]\s*$$".r
|
val wildcardRegex = raw"^\s*[*%].*[*%]\s*$$".r
|
||||||
searchRegex.findFirstIn(recordNameFilter).isDefined && wildcardRegex.findFirstIn(recordNameFilter).isEmpty
|
searchRegex.findFirstIn(recordNameFilter).isDefined && wildcardRegex.findFirstIn(recordNameFilter).isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def unchangedRecordSet(
|
||||||
|
existing: RecordSet,
|
||||||
|
updates: RecordSet
|
||||||
|
): Either[Throwable, Unit] =
|
||||||
|
Either.cond(
|
||||||
|
updates.typ == existing.typ &&
|
||||||
|
updates.records == existing.records &&
|
||||||
|
updates.id == existing.id &&
|
||||||
|
updates.zoneId == existing.zoneId &&
|
||||||
|
updates.name == existing.name &&
|
||||||
|
updates.ownerGroupId == existing.ownerGroupId &&
|
||||||
|
updates.ttl == existing.ttl,
|
||||||
|
(),
|
||||||
|
InvalidRequest("Cannot update RecordSet's if user not a member of ownership group. User can only request for ownership transfer")
|
||||||
|
)
|
||||||
|
|
||||||
|
def recordSetOwnerShipApproveStatus(
|
||||||
|
updates: RecordSet,
|
||||||
|
): Either[Throwable, Unit] =
|
||||||
|
Either.cond(
|
||||||
|
updates.recordSetGroupChange.map(_.ownerShipTransferStatus).getOrElse("<none>") != OwnerShipTransferStatus.ManuallyApproved &&
|
||||||
|
updates.recordSetGroupChange.map(_.ownerShipTransferStatus).getOrElse("<none>") != OwnerShipTransferStatus.AutoApproved &&
|
||||||
|
updates.recordSetGroupChange.map(_.ownerShipTransferStatus).getOrElse("<none>") != OwnerShipTransferStatus.ManuallyRejected,
|
||||||
|
(),
|
||||||
|
InvalidRequest("Cannot update RecordSet OwnerShip Status when request is cancelled.")
|
||||||
|
)
|
||||||
|
|
||||||
|
def unchangedRecordSetOwnershipStatus(
|
||||||
|
updates: RecordSet,
|
||||||
|
existing: RecordSet
|
||||||
|
): Either[Throwable, Unit] =
|
||||||
|
Either.cond(
|
||||||
|
updates.recordSetGroupChange == existing.recordSetGroupChange,
|
||||||
|
(),
|
||||||
|
InvalidRequest("Cannot update RecordSet OwnerShip Status when zone is not shared.")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import vinyldns.core.domain.record.RecordSetChangeStatus.RecordSetChangeStatus
|
|||||||
import vinyldns.core.domain.record.RecordSetChangeType.RecordSetChangeType
|
import vinyldns.core.domain.record.RecordSetChangeType.RecordSetChangeType
|
||||||
import vinyldns.core.domain.record.RecordSetStatus.RecordSetStatus
|
import vinyldns.core.domain.record.RecordSetStatus.RecordSetStatus
|
||||||
import vinyldns.core.domain.record.RecordType.RecordType
|
import vinyldns.core.domain.record.RecordType.RecordType
|
||||||
import vinyldns.core.domain.record.{RecordData, RecordSet, RecordSetChange}
|
import vinyldns.core.domain.record.{RecordData, RecordSet, RecordSetChange, OwnerShipTransfer}
|
||||||
import vinyldns.core.domain.zone.{ACLRuleInfo, AccessLevel, Zone, ZoneACL, ZoneChange, ZoneConnection}
|
import vinyldns.core.domain.zone.{ACLRuleInfo, AccessLevel, Zone, ZoneACL, ZoneChange, ZoneConnection}
|
||||||
import vinyldns.core.domain.zone.AccessLevel.AccessLevel
|
import vinyldns.core.domain.zone.AccessLevel.AccessLevel
|
||||||
import vinyldns.core.domain.zone.ZoneStatus.ZoneStatus
|
import vinyldns.core.domain.zone.ZoneStatus.ZoneStatus
|
||||||
@ -180,6 +180,7 @@ case class RecordSetListInfo(
|
|||||||
accessLevel: AccessLevel,
|
accessLevel: AccessLevel,
|
||||||
ownerGroupId: Option[String],
|
ownerGroupId: Option[String],
|
||||||
ownerGroupName: Option[String],
|
ownerGroupName: Option[String],
|
||||||
|
recordSetGroupChange: Option[OwnerShipTransfer],
|
||||||
fqdn: Option[String]
|
fqdn: Option[String]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -199,6 +200,7 @@ object RecordSetListInfo {
|
|||||||
accessLevel = accessLevel,
|
accessLevel = accessLevel,
|
||||||
ownerGroupId = recordSet.ownerGroupId,
|
ownerGroupId = recordSet.ownerGroupId,
|
||||||
ownerGroupName = recordSet.ownerGroupName,
|
ownerGroupName = recordSet.ownerGroupName,
|
||||||
|
recordSetGroupChange = recordSet.recordSetGroupChange,
|
||||||
fqdn = recordSet.fqdn
|
fqdn = recordSet.fqdn
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -216,6 +218,7 @@ case class RecordSetInfo(
|
|||||||
account: String,
|
account: String,
|
||||||
ownerGroupId: Option[String],
|
ownerGroupId: Option[String],
|
||||||
ownerGroupName: Option[String],
|
ownerGroupName: Option[String],
|
||||||
|
recordSetGroupChange: Option[OwnerShipTransfer],
|
||||||
fqdn: Option[String]
|
fqdn: Option[String]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -234,6 +237,7 @@ object RecordSetInfo {
|
|||||||
account = recordSet.account,
|
account = recordSet.account,
|
||||||
ownerGroupId = recordSet.ownerGroupId,
|
ownerGroupId = recordSet.ownerGroupId,
|
||||||
ownerGroupName = groupName,
|
ownerGroupName = groupName,
|
||||||
|
recordSetGroupChange = recordSet.recordSetGroupChange,
|
||||||
fqdn = recordSet.fqdn
|
fqdn = recordSet.fqdn
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -251,6 +255,7 @@ case class RecordSetGlobalInfo(
|
|||||||
account: String,
|
account: String,
|
||||||
ownerGroupId: Option[String],
|
ownerGroupId: Option[String],
|
||||||
ownerGroupName: Option[String],
|
ownerGroupName: Option[String],
|
||||||
|
recordSetGroupChange: Option[OwnerShipTransfer],
|
||||||
fqdn: Option[String],
|
fqdn: Option[String],
|
||||||
zoneName: String,
|
zoneName: String,
|
||||||
zoneShared: Boolean
|
zoneShared: Boolean
|
||||||
@ -276,6 +281,7 @@ object RecordSetGlobalInfo {
|
|||||||
account = recordSet.account,
|
account = recordSet.account,
|
||||||
ownerGroupId = recordSet.ownerGroupId,
|
ownerGroupId = recordSet.ownerGroupId,
|
||||||
ownerGroupName = groupName,
|
ownerGroupName = groupName,
|
||||||
|
recordSetGroupChange = recordSet.recordSetGroupChange,
|
||||||
fqdn = recordSet.fqdn,
|
fqdn = recordSet.fqdn,
|
||||||
zoneName = zoneName,
|
zoneName = zoneName,
|
||||||
zoneShared = zoneShared
|
zoneShared = zoneShared
|
||||||
|
@ -18,33 +18,25 @@ package vinyldns.api.notifier.email
|
|||||||
|
|
||||||
import vinyldns.core.notifier.{Notification, Notifier}
|
import vinyldns.core.notifier.{Notification, Notifier}
|
||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
import vinyldns.core.domain.batch.{
|
import cats.implicits._
|
||||||
BatchChange,
|
import cats.effect.IO
|
||||||
BatchChangeApprovalStatus,
|
import vinyldns.core.domain.batch.{BatchChange, BatchChangeApprovalStatus, SingleAddChange, SingleChange, SingleDeleteRRSetChange}
|
||||||
SingleAddChange,
|
import vinyldns.core.domain.membership.{GroupRepository, User, UserRepository}
|
||||||
SingleChange,
|
|
||||||
SingleDeleteRRSetChange
|
|
||||||
}
|
|
||||||
import vinyldns.core.domain.membership.UserRepository
|
|
||||||
import vinyldns.core.domain.membership.User
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
import javax.mail.internet.{InternetAddress, MimeMessage}
|
import javax.mail.internet.{InternetAddress, MimeMessage}
|
||||||
import javax.mail.{Address, Message, Session}
|
import javax.mail.{Address, Message, Session}
|
||||||
|
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
import vinyldns.core.domain.record.AData
|
import vinyldns.core.domain.record.{AAAAData, AData, CNAMEData, MXData, OwnerShipTransferStatus, PTRData, RecordData, RecordSetChange, TXTData}
|
||||||
import vinyldns.core.domain.record.AAAAData
|
import vinyldns.core.domain.record.OwnerShipTransferStatus.OwnerShipTransferStatus
|
||||||
import vinyldns.core.domain.record.CNAMEData
|
|
||||||
import vinyldns.core.domain.record.MXData
|
|
||||||
import vinyldns.core.domain.record.TXTData
|
|
||||||
import vinyldns.core.domain.record.PTRData
|
|
||||||
import vinyldns.core.domain.record.RecordData
|
|
||||||
import java.time.format.{DateTimeFormatter, FormatStyle}
|
import java.time.format.{DateTimeFormatter, FormatStyle}
|
||||||
import vinyldns.core.domain.batch.BatchChangeStatus._
|
import vinyldns.core.domain.batch.BatchChangeStatus._
|
||||||
import vinyldns.core.domain.batch.BatchChangeApprovalStatus._
|
import vinyldns.core.domain.batch.BatchChangeApprovalStatus._
|
||||||
|
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
|
|
||||||
class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepository: UserRepository)
|
class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepository: UserRepository, groupRepository: GroupRepository)
|
||||||
extends Notifier {
|
extends Notifier {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[EmailNotifier])
|
private val logger = LoggerFactory.getLogger(classOf[EmailNotifier])
|
||||||
@ -52,12 +44,15 @@ class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepositor
|
|||||||
def notify(notification: Notification[_]): IO[Unit] =
|
def notify(notification: Notification[_]): IO[Unit] =
|
||||||
notification.change match {
|
notification.change match {
|
||||||
case bc: BatchChange => sendBatchChangeNotification(bc)
|
case bc: BatchChange => sendBatchChangeNotification(bc)
|
||||||
|
case rsc: RecordSetChange => sendRecordSetOwnerTransferNotification(rsc)
|
||||||
case _ => IO.unit
|
case _ => IO.unit
|
||||||
}
|
}
|
||||||
|
|
||||||
def send(addresses: Address*)(buildMessage: Message => Message): IO[Unit] = IO {
|
|
||||||
|
def send(toAddresses: Address*)(ccAddresses: Address*)(buildMessage: Message => Message): IO[Unit] = IO {
|
||||||
val message = new MimeMessage(session)
|
val message = new MimeMessage(session)
|
||||||
message.setRecipients(Message.RecipientType.TO, addresses.toArray)
|
message.setRecipients(Message.RecipientType.TO, toAddresses.toArray)
|
||||||
|
message.setRecipients(Message.RecipientType.CC, ccAddresses.toArray)
|
||||||
message.setFrom(config.from)
|
message.setFrom(config.from)
|
||||||
buildMessage(message)
|
buildMessage(message)
|
||||||
message.saveChanges()
|
message.saveChanges()
|
||||||
@ -67,10 +62,10 @@ class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepositor
|
|||||||
transport.close()
|
transport.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
def sendBatchChangeNotification(bc: BatchChange): IO[Unit] =
|
def sendBatchChangeNotification(bc: BatchChange): IO[Unit] = {
|
||||||
userRepository.getUser(bc.userId).flatMap {
|
userRepository.getUser(bc.userId).flatMap {
|
||||||
case Some(UserWithEmail(email)) =>
|
case Some(UserWithEmail(email)) =>
|
||||||
send(email) { message =>
|
send(email)() { message =>
|
||||||
message.setSubject(s"VinylDNS Batch change ${bc.id} results")
|
message.setSubject(s"VinylDNS Batch change ${bc.id} results")
|
||||||
message.setContent(formatBatchChange(bc), "text/html")
|
message.setContent(formatBatchChange(bc), "text/html")
|
||||||
message
|
message
|
||||||
@ -81,9 +76,58 @@ class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepositor
|
|||||||
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 None => IO {
|
||||||
|
logger.warn(s"Unable to find user: ${bc.userId}")
|
||||||
|
}
|
||||||
case _ => IO.unit
|
case _ => IO.unit
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def sendRecordSetOwnerTransferNotification(rsc: RecordSetChange): IO[Unit] = {
|
||||||
|
for {
|
||||||
|
toGroup <- groupRepository.getGroup(rsc.recordSet.ownerGroupId.getOrElse("<none>"))
|
||||||
|
ccGroup <- groupRepository.getGroup(rsc.recordSet.recordSetGroupChange.map(_.requestedOwnerGroupId.getOrElse("<none>")).getOrElse("<none>"))
|
||||||
|
_ <- toGroup match {
|
||||||
|
case Some(group) =>
|
||||||
|
group.memberIds.toList.traverse { id =>
|
||||||
|
userRepository.getUser(id).flatMap {
|
||||||
|
case Some(UserWithEmail(toEmail)) =>
|
||||||
|
ccGroup match {
|
||||||
|
case Some(ccg) =>
|
||||||
|
ccg.memberIds.toList.traverse { id =>
|
||||||
|
userRepository.getUser(id).flatMap {
|
||||||
|
case Some(ccUser) =>
|
||||||
|
val ccEmail = ccUser.email.getOrElse("<none>")
|
||||||
|
send(toEmail)(new InternetAddress(ccEmail)) { message =>
|
||||||
|
message.setSubject(s"VinylDNS RecordSet change ${rsc.id} results")
|
||||||
|
message.setContent(formatRecordSetChange(rsc), "text/html")
|
||||||
|
message
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
IO.unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case None => IO.unit
|
||||||
|
}
|
||||||
|
case Some(user: User) if user.email.isDefined =>
|
||||||
|
IO {
|
||||||
|
logger.warn(
|
||||||
|
s"Unable to properly parse email for ${user.id}: ${user.email.getOrElse("<none>")}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
IO {
|
||||||
|
logger.warn(s"Unable to find user: ${rsc.userId}")
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
IO.unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case None => IO.unit // Handle case where toGroup is None
|
||||||
|
}
|
||||||
|
} yield ()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def formatBatchChange(bc: BatchChange): String = {
|
def formatBatchChange(bc: BatchChange): String = {
|
||||||
val sb = new StringBuilder
|
val sb = new StringBuilder
|
||||||
@ -93,7 +137,7 @@ class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepositor
|
|||||||
| ${bc.comments.map(comments => s"<b>Description:</b> $comments</br>").getOrElse("")}
|
| ${bc.comments.map(comments => s"<b>Description:</b> $comments</br>").getOrElse("")}
|
||||||
| <b>Created:</b> ${DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withZone(ZoneId.systemDefault()).format(bc.createdTimestamp)} <br/>
|
| <b>Created:</b> ${DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withZone(ZoneId.systemDefault()).format(bc.createdTimestamp)} <br/>
|
||||||
| <b>Id:</b> ${bc.id}<br/>
|
| <b>Id:</b> ${bc.id}<br/>
|
||||||
| <b>Status:</b> ${formatStatus(bc.approvalStatus, bc.status)}<br/>""".stripMargin)
|
| <b>Status:</b> ${formatBatchStatus(bc.approvalStatus, bc.status)}<br/>""".stripMargin)
|
||||||
|
|
||||||
// For manually reviewed e-mails, add additional info; e-mails are not sent for pending batch changes
|
// For manually reviewed e-mails, add additional info; e-mails are not sent for pending batch changes
|
||||||
if (bc.approvalStatus != AutoApproved) {
|
if (bc.approvalStatus != AutoApproved) {
|
||||||
@ -125,7 +169,8 @@ class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepositor
|
|||||||
sb.toString
|
sb.toString
|
||||||
}
|
}
|
||||||
|
|
||||||
def formatStatus(approval: BatchChangeApprovalStatus, status: BatchChangeStatus): String =
|
|
||||||
|
def formatBatchStatus(approval: BatchChangeApprovalStatus, status: BatchChangeStatus): String =
|
||||||
(approval, status) match {
|
(approval, status) match {
|
||||||
case (ManuallyRejected, _) => "Rejected"
|
case (ManuallyRejected, _) => "Rejected"
|
||||||
case (BatchChangeApprovalStatus.PendingReview, _) => "Pending Review"
|
case (BatchChangeApprovalStatus.PendingReview, _) => "Pending Review"
|
||||||
@ -133,6 +178,28 @@ class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepositor
|
|||||||
case (_, status) => status.toString
|
case (_, status) => status.toString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def formatRecordSetChange(rsc: RecordSetChange): String = {
|
||||||
|
|
||||||
|
val sb = new StringBuilder
|
||||||
|
sb.append(s"""<h1>RecordSet Ownership Transfer</h1>
|
||||||
|
| <b>Submitter:</b> ${ userRepository.getUser(rsc.userId).map(_.get.userName)}
|
||||||
|
| <b>Id:</b> ${rsc.id}<br/>
|
||||||
|
| <b>Submitted time:</b> ${DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withZone(ZoneId.systemDefault()).format(rsc.created)} <br/>
|
||||||
|
| <b>OwnerShip Current Group:</b> ${rsc.recordSet.ownerGroupId.getOrElse("none")} <br/>
|
||||||
|
| <b>OwnerShip Transfer Group:</b> ${rsc.recordSet.recordSetGroupChange.map(_.requestedOwnerGroupId.getOrElse("none")).getOrElse("none")} <br/>
|
||||||
|
| <b>OwnerShip Transfer Status:</b> ${formatOwnerShipStatus(rsc.recordSet.recordSetGroupChange.map(_.ownerShipTransferStatus).get)}<br/>
|
||||||
|
""".stripMargin)
|
||||||
|
sb.toString
|
||||||
|
}
|
||||||
|
|
||||||
|
def formatOwnerShipStatus(status: OwnerShipTransferStatus): String =
|
||||||
|
status match {
|
||||||
|
case OwnerShipTransferStatus.ManuallyRejected => "Rejected"
|
||||||
|
case OwnerShipTransferStatus.PendingReview => "Pending Review"
|
||||||
|
case OwnerShipTransferStatus.ManuallyApproved => "Approved"
|
||||||
|
case OwnerShipTransferStatus.Cancelled => "Cancelled"
|
||||||
|
}
|
||||||
|
|
||||||
def formatSingleChange(sc: SingleChange, index: Int): String = sc match {
|
def formatSingleChange(sc: SingleChange, index: Int): String = sc match {
|
||||||
case SingleAddChange(
|
case SingleAddChange(
|
||||||
_,
|
_,
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
package vinyldns.api.notifier.email
|
package vinyldns.api.notifier.email
|
||||||
|
|
||||||
import vinyldns.core.notifier.{Notifier, NotifierConfig, NotifierProvider}
|
import vinyldns.core.notifier.{Notifier, NotifierConfig, NotifierProvider}
|
||||||
import vinyldns.core.domain.membership.UserRepository
|
import vinyldns.core.domain.membership.{GroupRepository, UserRepository}
|
||||||
import pureconfig._
|
import pureconfig._
|
||||||
import pureconfig.generic.auto._
|
import pureconfig.generic.auto._
|
||||||
import pureconfig.module.catseffect.syntax._
|
import pureconfig.module.catseffect.syntax._
|
||||||
@ -30,13 +30,13 @@ class EmailNotifierProvider extends NotifierProvider {
|
|||||||
private implicit val cs: ContextShift[IO] =
|
private implicit val cs: ContextShift[IO] =
|
||||||
IO.contextShift(scala.concurrent.ExecutionContext.global)
|
IO.contextShift(scala.concurrent.ExecutionContext.global)
|
||||||
|
|
||||||
def load(config: NotifierConfig, userRepository: UserRepository): IO[Notifier] =
|
def load(config: NotifierConfig, userRepository: UserRepository, groupRepository: GroupRepository): IO[Notifier] =
|
||||||
for {
|
for {
|
||||||
emailConfig <- Blocker[IO].use(
|
emailConfig <- Blocker[IO].use(
|
||||||
ConfigSource.fromConfig(config.settings).loadF[IO, EmailNotifierConfig](_)
|
ConfigSource.fromConfig(config.settings).loadF[IO, EmailNotifierConfig](_)
|
||||||
)
|
)
|
||||||
session <- createSession(emailConfig)
|
session <- createSession(emailConfig)
|
||||||
} yield new EmailNotifier(emailConfig, session, userRepository)
|
} yield new EmailNotifier(emailConfig, session, userRepository, groupRepository)
|
||||||
|
|
||||||
def createSession(config: EmailNotifierConfig): IO[Session] = IO {
|
def createSession(config: EmailNotifierConfig): IO[Session] = IO {
|
||||||
Session.getInstance(config.smtp)
|
Session.getInstance(config.smtp)
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
package vinyldns.api.notifier.sns
|
package vinyldns.api.notifier.sns
|
||||||
|
|
||||||
import vinyldns.core.notifier.{Notifier, NotifierConfig, NotifierProvider}
|
import vinyldns.core.notifier.{Notifier, NotifierConfig, NotifierProvider}
|
||||||
import vinyldns.core.domain.membership.UserRepository
|
import vinyldns.core.domain.membership.{GroupRepository, UserRepository}
|
||||||
import pureconfig._
|
import pureconfig._
|
||||||
import pureconfig.generic.auto._
|
import pureconfig.generic.auto._
|
||||||
import pureconfig.module.catseffect.syntax._
|
import pureconfig.module.catseffect.syntax._
|
||||||
@ -35,7 +35,7 @@ class SnsNotifierProvider extends NotifierProvider {
|
|||||||
IO.contextShift(scala.concurrent.ExecutionContext.global)
|
IO.contextShift(scala.concurrent.ExecutionContext.global)
|
||||||
private val logger = LoggerFactory.getLogger(classOf[SnsNotifierProvider])
|
private val logger = LoggerFactory.getLogger(classOf[SnsNotifierProvider])
|
||||||
|
|
||||||
def load(config: NotifierConfig, userRepository: UserRepository): IO[Notifier] =
|
def load(config: NotifierConfig, userRepository: UserRepository, groupRepository: GroupRepository): IO[Notifier] =
|
||||||
for {
|
for {
|
||||||
snsConfig <- Blocker[IO].use(
|
snsConfig <- Blocker[IO].use(
|
||||||
ConfigSource.fromConfig(config.settings).loadF[IO, SnsNotifierConfig](_)
|
ConfigSource.fromConfig(config.settings).loadF[IO, SnsNotifierConfig](_)
|
||||||
|
@ -31,6 +31,8 @@ import vinyldns.core.domain.{EncryptFromJson, Encrypted, Fqdn}
|
|||||||
import vinyldns.core.domain.record._
|
import vinyldns.core.domain.record._
|
||||||
import vinyldns.core.domain.zone._
|
import vinyldns.core.domain.zone._
|
||||||
import vinyldns.core.Messages._
|
import vinyldns.core.Messages._
|
||||||
|
import vinyldns.core.domain.record.OwnerShipTransferStatus
|
||||||
|
import vinyldns.core.domain.record.OwnerShipTransferStatus.OwnerShipTransferStatus
|
||||||
|
|
||||||
trait DnsJsonProtocol extends JsonValidation {
|
trait DnsJsonProtocol extends JsonValidation {
|
||||||
import vinyldns.core.domain.record.RecordType._
|
import vinyldns.core.domain.record.RecordType._
|
||||||
@ -42,11 +44,13 @@ trait DnsJsonProtocol extends JsonValidation {
|
|||||||
AlgorithmSerializer,
|
AlgorithmSerializer,
|
||||||
EncryptedSerializer,
|
EncryptedSerializer,
|
||||||
RecordSetSerializer,
|
RecordSetSerializer,
|
||||||
|
ownerShipTransferSerializer,
|
||||||
RecordSetListInfoSerializer,
|
RecordSetListInfoSerializer,
|
||||||
RecordSetGlobalInfoSerializer,
|
RecordSetGlobalInfoSerializer,
|
||||||
RecordSetInfoSerializer,
|
RecordSetInfoSerializer,
|
||||||
RecordSetChangeSerializer,
|
RecordSetChangeSerializer,
|
||||||
JsonEnumV(ZoneStatus),
|
JsonEnumV(ZoneStatus),
|
||||||
|
JsonEnumV(OwnerShipTransferStatus),
|
||||||
JsonEnumV(ZoneChangeStatus),
|
JsonEnumV(ZoneChangeStatus),
|
||||||
JsonEnumV(RecordSetStatus),
|
JsonEnumV(RecordSetStatus),
|
||||||
JsonEnumV(RecordSetChangeStatus),
|
JsonEnumV(RecordSetChangeStatus),
|
||||||
@ -232,6 +236,7 @@ trait DnsJsonProtocol extends JsonValidation {
|
|||||||
(js \ "id").default[String](UUID.randomUUID().toString),
|
(js \ "id").default[String](UUID.randomUUID().toString),
|
||||||
(js \ "account").default[String]("system"),
|
(js \ "account").default[String]("system"),
|
||||||
(js \ "ownerGroupId").optional[String],
|
(js \ "ownerGroupId").optional[String],
|
||||||
|
(js \ "recordSetGroupChange").optional[OwnerShipTransfer],
|
||||||
(js \ "fqdn").optional[String]
|
(js \ "fqdn").optional[String]
|
||||||
).mapN(RecordSet.apply)
|
).mapN(RecordSet.apply)
|
||||||
|
|
||||||
@ -256,9 +261,23 @@ trait DnsJsonProtocol extends JsonValidation {
|
|||||||
("id" -> rs.id) ~
|
("id" -> rs.id) ~
|
||||||
("account" -> rs.account) ~
|
("account" -> rs.account) ~
|
||||||
("ownerGroupId" -> rs.ownerGroupId) ~
|
("ownerGroupId" -> rs.ownerGroupId) ~
|
||||||
|
("recordSetGroupChange" -> Extraction.decompose(rs.recordSetGroupChange)) ~
|
||||||
("fqdn" -> rs.fqdn)
|
("fqdn" -> rs.fqdn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case object ownerShipTransferSerializer extends ValidationSerializer[OwnerShipTransfer] {
|
||||||
|
override def fromJson(js: JValue): ValidatedNel[String, OwnerShipTransfer] =
|
||||||
|
(
|
||||||
|
(js \ "ownerShipTransferStatus").required[OwnerShipTransferStatus]("Missing ownerShipTransfer.ownerShipTransferStatus"),
|
||||||
|
(js \ "requestedOwnerGroupId").optional[String],
|
||||||
|
).mapN(OwnerShipTransfer.apply)
|
||||||
|
|
||||||
|
override def toJson(rsa: OwnerShipTransfer): JValue =
|
||||||
|
("ownerShipTransferStatus" -> Extraction.decompose(rsa.ownerShipTransferStatus)) ~
|
||||||
|
("requestedOwnerGroupId" -> Extraction.decompose(rsa.requestedOwnerGroupId))
|
||||||
|
}
|
||||||
|
|
||||||
case object RecordSetListInfoSerializer extends ValidationSerializer[RecordSetListInfo] {
|
case object RecordSetListInfoSerializer extends ValidationSerializer[RecordSetListInfo] {
|
||||||
override def fromJson(js: JValue): ValidatedNel[String, RecordSetListInfo] =
|
override def fromJson(js: JValue): ValidatedNel[String, RecordSetListInfo] =
|
||||||
(
|
(
|
||||||
@ -280,6 +299,7 @@ trait DnsJsonProtocol extends JsonValidation {
|
|||||||
("accessLevel" -> rs.accessLevel.toString) ~
|
("accessLevel" -> rs.accessLevel.toString) ~
|
||||||
("ownerGroupId" -> rs.ownerGroupId) ~
|
("ownerGroupId" -> rs.ownerGroupId) ~
|
||||||
("ownerGroupName" -> rs.ownerGroupName) ~
|
("ownerGroupName" -> rs.ownerGroupName) ~
|
||||||
|
("recordSetGroupChange" -> Extraction.decompose(rs.recordSetGroupChange)) ~
|
||||||
("fqdn" -> rs.fqdn)
|
("fqdn" -> rs.fqdn)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,6 +321,7 @@ trait DnsJsonProtocol extends JsonValidation {
|
|||||||
("account" -> rs.account) ~
|
("account" -> rs.account) ~
|
||||||
("ownerGroupId" -> rs.ownerGroupId) ~
|
("ownerGroupId" -> rs.ownerGroupId) ~
|
||||||
("ownerGroupName" -> rs.ownerGroupName) ~
|
("ownerGroupName" -> rs.ownerGroupName) ~
|
||||||
|
("recordSetGroupChange" -> Extraction.decompose(rs.recordSetGroupChange)) ~
|
||||||
("fqdn" -> rs.fqdn)
|
("fqdn" -> rs.fqdn)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,6 +347,7 @@ trait DnsJsonProtocol extends JsonValidation {
|
|||||||
("account" -> rs.account) ~
|
("account" -> rs.account) ~
|
||||||
("ownerGroupId" -> rs.ownerGroupId) ~
|
("ownerGroupId" -> rs.ownerGroupId) ~
|
||||||
("ownerGroupName" -> rs.ownerGroupName) ~
|
("ownerGroupName" -> rs.ownerGroupName) ~
|
||||||
|
("recordSetGroupChange" -> Extraction.decompose(rs.recordSetGroupChange)) ~
|
||||||
("fqdn" -> rs.fqdn) ~
|
("fqdn" -> rs.fqdn) ~
|
||||||
("zoneName" -> rs.zoneName) ~
|
("zoneName" -> rs.zoneName) ~
|
||||||
("zoneShared" -> rs.zoneShared)
|
("zoneShared" -> rs.zoneShared)
|
||||||
|
@ -1927,6 +1927,561 @@ def test_update_from_acl_for_shared_zone_passes(shared_zone_test_context):
|
|||||||
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
def test_update_owner_group_transfer_auto_approved(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test auto approve ownerShip transfer, for shared zones
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
zone = shared_zone_test_context.shared_zone
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
ok_group = shared_zone_test_context.ok_group
|
||||||
|
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(zone, "test_shared_admin_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
record_json["ownerGroupId"] = shared_group["id"]
|
||||||
|
create_response = shared_client.create_recordset(record_json, status=202)
|
||||||
|
update = shared_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "AutoApproved",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
update_response = shared_client.update_recordset(update, status=202)
|
||||||
|
update_rs = shared_client.wait_until_recordset_change_status(update_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs["recordSetGroupChange"], is_(recordset_group_change_json))
|
||||||
|
assert_that(update_rs["ownerGroupId"], is_(ok_group["id"]))
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_owner_group_transfer_request(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test requesting ownerShip transfer, for shared zones
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
dummy_client = shared_zone_test_context.dummy_vinyldns_client
|
||||||
|
zone = shared_zone_test_context.shared_zone
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
dummy_group = shared_zone_test_context.dummy_group
|
||||||
|
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(zone, "test_shared_admin_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
record_json["ownerGroupId"] = dummy_group["id"]
|
||||||
|
|
||||||
|
create_response = dummy_client.create_recordset(record_json, status=202)
|
||||||
|
update = dummy_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update["ownerGroupId"], is_(dummy_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Requested",
|
||||||
|
"requestedOwnerGroupId": shared_group["id"]}
|
||||||
|
recordset_group_change_pending_review_json = {"ownerShipTransferStatus": "PendingReview",
|
||||||
|
"requestedOwnerGroupId": shared_group["id"]}
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
|
||||||
|
update_response = shared_client.update_recordset(update, status=202)
|
||||||
|
update_rs = shared_client.wait_until_recordset_change_status(update_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs["recordSetGroupChange"], is_(recordset_group_change_pending_review_json))
|
||||||
|
assert_that(update_rs["ownerGroupId"], is_(dummy_group["id"]))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_request_owner_group_transfer_manually_approved(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test approving ownerShip transfer request, for shared zones
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
ok_client = shared_zone_test_context.ok_vinyldns_client
|
||||||
|
zone = shared_zone_test_context.shared_zone
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
ok_group = shared_zone_test_context.ok_group
|
||||||
|
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(zone, "test_shared_admin_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
record_json["ownerGroupId"] = shared_group["id"]
|
||||||
|
|
||||||
|
create_response = shared_client.create_recordset(record_json, status=202)
|
||||||
|
update = shared_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Requested",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
recordset_group_change_pending_review_json = {"ownerShipTransferStatus": "PendingReview",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
|
||||||
|
update_response = ok_client.update_recordset(update, status=202)
|
||||||
|
update_rs = ok_client.wait_until_recordset_change_status(update_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs["recordSetGroupChange"], is_(recordset_group_change_pending_review_json))
|
||||||
|
assert_that(update_rs["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "ManuallyApproved"}
|
||||||
|
recordset_group_change_manually_approved_json = {"ownerShipTransferStatus": "ManuallyApproved",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
update_rs["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
update_rs_response = shared_client.update_recordset(update_rs, status=202)
|
||||||
|
update_rs_ownership = shared_client.wait_until_recordset_change_status(update_rs_response, "Complete")[
|
||||||
|
"recordSet"]
|
||||||
|
assert_that(update_rs_ownership["recordSetGroupChange"], is_(recordset_group_change_manually_approved_json))
|
||||||
|
assert_that(update_rs_ownership["ownerGroupId"], is_(ok_group["id"]))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_request_owner_group_transfer_manually_rejected(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test rejecting ownerShip transfer request, for shared zones
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
ok_client = shared_zone_test_context.ok_vinyldns_client
|
||||||
|
zone = shared_zone_test_context.shared_zone
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
ok_group = shared_zone_test_context.ok_group
|
||||||
|
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(zone, "test_shared_admin_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
record_json["ownerGroupId"] = shared_group["id"]
|
||||||
|
|
||||||
|
create_response = shared_client.create_recordset(record_json, status=202)
|
||||||
|
update = shared_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Requested",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
recordset_group_change_pending_review_json = {"ownerShipTransferStatus": "PendingReview",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
|
||||||
|
update_response = ok_client.update_recordset(update, status=202)
|
||||||
|
update_rs = ok_client.wait_until_recordset_change_status(update_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs["recordSetGroupChange"], is_(recordset_group_change_pending_review_json))
|
||||||
|
assert_that(update_rs["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "ManuallyRejected"}
|
||||||
|
recordset_group_change_manually_rejected_json = {"ownerShipTransferStatus": "ManuallyRejected",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
update_rs["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
update_rs_response = shared_client.update_recordset(update_rs, status=202)
|
||||||
|
update_rs_ownership = shared_client.wait_until_recordset_change_status(update_rs_response, "Complete")[
|
||||||
|
"recordSet"]
|
||||||
|
assert_that(update_rs_ownership["recordSetGroupChange"], is_(recordset_group_change_manually_rejected_json))
|
||||||
|
assert_that(update_rs_ownership["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_request_owner_group_transfer_cancelled(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test cancelling ownerShip transfer request
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
ok_client = shared_zone_test_context.ok_vinyldns_client
|
||||||
|
zone = shared_zone_test_context.shared_zone
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
ok_group = shared_zone_test_context.ok_group
|
||||||
|
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(zone, "test_shared_admin_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
record_json["ownerGroupId"] = shared_group["id"]
|
||||||
|
|
||||||
|
create_response = shared_client.create_recordset(record_json, status=202)
|
||||||
|
update = shared_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Requested",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
recordset_group_change_pending_review_json = {"ownerShipTransferStatus": "PendingReview",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
|
||||||
|
update_response = ok_client.update_recordset(update, status=202)
|
||||||
|
update_rs = ok_client.wait_until_recordset_change_status(update_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs["recordSetGroupChange"], is_(recordset_group_change_pending_review_json))
|
||||||
|
assert_that(update_rs["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Cancelled"}
|
||||||
|
recordset_group_change_cancelled_json = {"ownerShipTransferStatus": "Cancelled",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
update_rs["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
update_rs_response = ok_client.update_recordset(update_rs, status=202)
|
||||||
|
update_rs_ownership = ok_client.wait_until_recordset_change_status(update_rs_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs_ownership["recordSetGroupChange"], is_(recordset_group_change_cancelled_json))
|
||||||
|
assert_that(update_rs_ownership["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_owner_group_transfer_approval_to_group_a_user_is_not_in_fails(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test approving ownerShip transfer request, for user not a member of owner group
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
dummy_client = shared_zone_test_context.dummy_vinyldns_client
|
||||||
|
zone = shared_zone_test_context.shared_zone
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
dummy_group = shared_zone_test_context.dummy_group
|
||||||
|
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(zone, "test_shared_admin_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
record_json["ownerGroupId"] = dummy_group["id"]
|
||||||
|
|
||||||
|
create_response = dummy_client.create_recordset(record_json, status=202)
|
||||||
|
update = dummy_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update["ownerGroupId"], is_(dummy_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Requested",
|
||||||
|
"requestedOwnerGroupId": shared_group["id"]}
|
||||||
|
recordset_group_change_pending_review_json = {"ownerShipTransferStatus": "PendingReview",
|
||||||
|
"requestedOwnerGroupId": shared_group["id"]}
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
|
||||||
|
update_response = shared_client.update_recordset(update, status=202)
|
||||||
|
update_rs = shared_client.wait_until_recordset_change_status(update_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs["recordSetGroupChange"], is_(recordset_group_change_pending_review_json))
|
||||||
|
assert_that(update_rs["ownerGroupId"], is_(dummy_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_approved_json = {"ownerShipTransferStatus": "ManuallyApproved"}
|
||||||
|
|
||||||
|
update_rs["recordSetGroupChange"] = recordset_group_change_approved_json
|
||||||
|
error = shared_client.update_recordset(update_rs, status=422)
|
||||||
|
assert_that(error, is_(f"User not in record owner group with id \"{dummy_group['id']}\""))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_owner_group_transfer_reject_to_group_a_user_is_not_in_fails(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test rejecting ownerShip transfer request, for user not a member of owner group
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
dummy_client = shared_zone_test_context.dummy_vinyldns_client
|
||||||
|
zone = shared_zone_test_context.shared_zone
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
dummy_group = shared_zone_test_context.dummy_group
|
||||||
|
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(zone, "test_shared_admin_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
record_json["ownerGroupId"] = dummy_group["id"]
|
||||||
|
|
||||||
|
create_response = dummy_client.create_recordset(record_json, status=202)
|
||||||
|
update = dummy_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update["ownerGroupId"], is_(dummy_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Requested",
|
||||||
|
"requestedOwnerGroupId": shared_group["id"]}
|
||||||
|
recordset_group_change_pending_review_json = {"ownerShipTransferStatus": "PendingReview",
|
||||||
|
"requestedOwnerGroupId": shared_group["id"]}
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
|
||||||
|
update_response = shared_client.update_recordset(update, status=202)
|
||||||
|
update_rs = shared_client.wait_until_recordset_change_status(update_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs["recordSetGroupChange"], is_(recordset_group_change_pending_review_json))
|
||||||
|
assert_that(update_rs["ownerGroupId"], is_(dummy_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_approved_json = {"ownerShipTransferStatus": "ManuallyRejected"}
|
||||||
|
update_rs["recordSetGroupChange"] = recordset_group_change_approved_json
|
||||||
|
error = shared_client.update_recordset(update_rs, status=422)
|
||||||
|
assert_that(error, is_(f"User not in record owner group with id \"{dummy_group['id']}\""))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_owner_group_transfer_auto_approved_to_group_a_user_is_not_in_fails(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test approving ownerShip transfer request, for user not a member of owner group
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
dummy_client = shared_zone_test_context.dummy_vinyldns_client
|
||||||
|
zone = shared_zone_test_context.shared_zone
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
dummy_group = shared_zone_test_context.dummy_group
|
||||||
|
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(zone, "test_shared_admin_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
record_json["ownerGroupId"] = dummy_group["id"]
|
||||||
|
|
||||||
|
create_response = dummy_client.create_recordset(record_json, status=202)
|
||||||
|
update = dummy_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update["ownerGroupId"], is_(dummy_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Requested",
|
||||||
|
"requestedOwnerGroupId": shared_group["id"]}
|
||||||
|
recordset_group_change_pending_review_json = {"ownerShipTransferStatus": "PendingReview",
|
||||||
|
"requestedOwnerGroupId": shared_group["id"]}
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
|
||||||
|
update_response = shared_client.update_recordset(update, status=202)
|
||||||
|
update_rs = shared_client.wait_until_recordset_change_status(update_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs["recordSetGroupChange"], is_(recordset_group_change_pending_review_json))
|
||||||
|
assert_that(update_rs["ownerGroupId"], is_(dummy_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_approved_json = {"ownerShipTransferStatus": "AutoApproved"}
|
||||||
|
|
||||||
|
update_rs["recordSetGroupChange"] = recordset_group_change_approved_json
|
||||||
|
error = shared_client.update_recordset(update_rs, status=422)
|
||||||
|
assert_that(error, is_(f"Record owner group with id \"{dummy_group['id']}\" not found"))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_owner_group_transfer_approved_when_request_cancelled_in_fails(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test approving ownerShip transfer, for cancelled request
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
ok_client = shared_zone_test_context.ok_vinyldns_client
|
||||||
|
zone = shared_zone_test_context.shared_zone
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
ok_group = shared_zone_test_context.ok_group
|
||||||
|
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(zone, "test_shared_admin_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
record_json["ownerGroupId"] = shared_group["id"]
|
||||||
|
|
||||||
|
create_response = shared_client.create_recordset(record_json, status=202)
|
||||||
|
update = shared_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Requested",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
recordset_group_change_pending_review_json = {"ownerShipTransferStatus": "PendingReview",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
|
||||||
|
update_response = ok_client.update_recordset(update, status=202)
|
||||||
|
update_rs = ok_client.wait_until_recordset_change_status(update_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs["recordSetGroupChange"], is_(recordset_group_change_pending_review_json))
|
||||||
|
assert_that(update_rs["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Cancelled"}
|
||||||
|
recordset_group_change_cancelled_json = {"ownerShipTransferStatus": "Cancelled",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
update_rs["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
update_rs_response = ok_client.update_recordset(update_rs, status=202)
|
||||||
|
update_rs_ownership = ok_client.wait_until_recordset_change_status(update_rs_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs_ownership["recordSetGroupChange"], is_(recordset_group_change_cancelled_json))
|
||||||
|
assert_that(update_rs_ownership["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "ManuallyApproved"}
|
||||||
|
|
||||||
|
update_rs["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
error = ok_client.update_recordset(update_rs, status=422)
|
||||||
|
assert_that(error, is_("Cannot update RecordSet OwnerShip Status when request is cancelled."))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_owner_group_transfer_rejected_when_request_cancelled_in_fails(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test rejecting ownerShip transfer, for cancelled request
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
ok_client = shared_zone_test_context.ok_vinyldns_client
|
||||||
|
zone = shared_zone_test_context.shared_zone
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
ok_group = shared_zone_test_context.ok_group
|
||||||
|
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(zone, "test_shared_admin_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
record_json["ownerGroupId"] = shared_group["id"]
|
||||||
|
|
||||||
|
create_response = shared_client.create_recordset(record_json, status=202)
|
||||||
|
update = shared_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Requested",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
recordset_group_change_pending_review_json = {"ownerShipTransferStatus": "PendingReview",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
|
||||||
|
update_response = ok_client.update_recordset(update, status=202)
|
||||||
|
update_rs = ok_client.wait_until_recordset_change_status(update_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs["recordSetGroupChange"], is_(recordset_group_change_pending_review_json))
|
||||||
|
assert_that(update_rs["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Cancelled"}
|
||||||
|
recordset_group_change_cancelled_json = {"ownerShipTransferStatus": "Cancelled",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
update_rs["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
update_rs_response = ok_client.update_recordset(update_rs, status=202)
|
||||||
|
update_rs_ownership = ok_client.wait_until_recordset_change_status(update_rs_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs_ownership["recordSetGroupChange"], is_(recordset_group_change_cancelled_json))
|
||||||
|
assert_that(update_rs_ownership["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "ManuallyRejected"}
|
||||||
|
|
||||||
|
update_rs["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
error = ok_client.update_recordset(update_rs, status=422)
|
||||||
|
assert_that(error, is_("Cannot update RecordSet OwnerShip Status when request is cancelled."))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_owner_group_transfer_auto_approved_when_request_cancelled_in_fails(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test auto_approving ownerShip transfer, for cancelled request
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
ok_client = shared_zone_test_context.ok_vinyldns_client
|
||||||
|
zone = shared_zone_test_context.shared_zone
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
ok_group = shared_zone_test_context.ok_group
|
||||||
|
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(zone, "test_shared_admin_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
record_json["ownerGroupId"] = shared_group["id"]
|
||||||
|
|
||||||
|
create_response = shared_client.create_recordset(record_json, status=202)
|
||||||
|
update = shared_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Requested",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
recordset_group_change_pending_review_json = {"ownerShipTransferStatus": "PendingReview",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
|
||||||
|
update_response = ok_client.update_recordset(update, status=202)
|
||||||
|
update_rs = ok_client.wait_until_recordset_change_status(update_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs["recordSetGroupChange"], is_(recordset_group_change_pending_review_json))
|
||||||
|
assert_that(update_rs["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Cancelled"}
|
||||||
|
recordset_group_change_cancelled_json = {"ownerShipTransferStatus": "Cancelled",
|
||||||
|
"requestedOwnerGroupId": ok_group["id"]}
|
||||||
|
update_rs["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
update_rs_response = ok_client.update_recordset(update_rs, status=202)
|
||||||
|
update_rs_ownership = ok_client.wait_until_recordset_change_status(update_rs_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update_rs_ownership["recordSetGroupChange"], is_(recordset_group_change_cancelled_json))
|
||||||
|
assert_that(update_rs_ownership["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "AutoApproved"}
|
||||||
|
|
||||||
|
update_rs["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
error = ok_client.update_recordset(update_rs, status=422)
|
||||||
|
assert_that(error, is_("Cannot update RecordSet OwnerShip Status when request is cancelled."))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_owner_group_transfer_on_non_shared_zones_in_fails(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test that requesting ownerShip transfer for non shared zones
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
ok_client = shared_zone_test_context.ok_vinyldns_client
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
ok_zone = shared_zone_test_context.ok_zone
|
||||||
|
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(ok_zone, "test_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
|
||||||
|
create_response = ok_client.create_recordset(record_json, status=202)
|
||||||
|
update = ok_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Requested",
|
||||||
|
"requestedOwnerGroupId": shared_group["id"]}
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
|
||||||
|
error = shared_client.update_recordset(update, status=422)
|
||||||
|
assert_that(error, is_("Cannot update RecordSet OwnerShip Status when zone is not shared."))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_owner_group_transfer_and_ttl_on_user_not_in_owner_group_in_fails(shared_zone_test_context):
|
||||||
|
"""
|
||||||
|
Test that updating record "i.e.ttl" with requesting ownerShip transfer, where user not in the member of the owner group
|
||||||
|
"""
|
||||||
|
shared_client = shared_zone_test_context.shared_zone_vinyldns_client
|
||||||
|
dummy_client = shared_zone_test_context.dummy_vinyldns_client
|
||||||
|
zone = shared_zone_test_context.shared_zone
|
||||||
|
shared_group = shared_zone_test_context.shared_record_group
|
||||||
|
dummy_group = shared_zone_test_context.dummy_group
|
||||||
|
update_rs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
record_json = create_recordset(zone, "test_shared_admin_update_success", "A", [{"address": "1.1.1.1"}])
|
||||||
|
record_json["ownerGroupId"] = shared_group["id"]
|
||||||
|
|
||||||
|
create_response = shared_client.create_recordset(record_json, status=202)
|
||||||
|
update = shared_client.wait_until_recordset_change_status(create_response, "Complete")["recordSet"]
|
||||||
|
assert_that(update["ownerGroupId"], is_(shared_group["id"]))
|
||||||
|
|
||||||
|
recordset_group_change_json = {"ownerShipTransferStatus": "Requested",
|
||||||
|
"requestedOwnerGroupId": dummy_group["id"]}
|
||||||
|
update["recordSetGroupChange"] = recordset_group_change_json
|
||||||
|
update["ttl"] = update["ttl"] + 100
|
||||||
|
|
||||||
|
error = dummy_client.update_recordset(update, status=422)
|
||||||
|
assert_that(error, is_(f"Cannot update RecordSet's if user not a member of ownership group. User can only request for ownership transfer"))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if update_rs:
|
||||||
|
delete_result = shared_client.delete_recordset(zone["id"], update_rs["id"], status=202)
|
||||||
|
shared_client.wait_until_recordset_change_status(delete_result, "Complete")
|
||||||
|
|
||||||
|
|
||||||
def test_update_to_no_group_owner_passes(shared_zone_test_context):
|
def test_update_to_no_group_owner_passes(shared_zone_test_context):
|
||||||
"""
|
"""
|
||||||
|
@ -40,6 +40,8 @@ import vinyldns.core.domain.membership.{GroupRepository, ListUsersResults, UserR
|
|||||||
import vinyldns.core.domain.record._
|
import vinyldns.core.domain.record._
|
||||||
import vinyldns.core.domain.zone._
|
import vinyldns.core.domain.zone._
|
||||||
import vinyldns.core.queue.MessageQueue
|
import vinyldns.core.queue.MessageQueue
|
||||||
|
import vinyldns.core.notifier.{AllNotifiers, Notification, Notifier}
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
|
||||||
class RecordSetServiceSpec
|
class RecordSetServiceSpec
|
||||||
extends AnyWordSpec
|
extends AnyWordSpec
|
||||||
@ -47,6 +49,7 @@ class RecordSetServiceSpec
|
|||||||
with Matchers
|
with Matchers
|
||||||
with MockitoSugar
|
with MockitoSugar
|
||||||
with BeforeAndAfterEach {
|
with BeforeAndAfterEach {
|
||||||
|
private implicit val contextShift: ContextShift[IO] = IO.contextShift(ExecutionContext.global)
|
||||||
|
|
||||||
private val mockZoneRepo = mock[ZoneRepository]
|
private val mockZoneRepo = mock[ZoneRepository]
|
||||||
private val mockGroupRepo = mock[GroupRepository]
|
private val mockGroupRepo = mock[GroupRepository]
|
||||||
@ -58,6 +61,8 @@ class RecordSetServiceSpec
|
|||||||
private val mockBackend =
|
private val mockBackend =
|
||||||
mock[Backend]
|
mock[Backend]
|
||||||
private val mockBackendResolver = mock[BackendResolver]
|
private val mockBackendResolver = mock[BackendResolver]
|
||||||
|
private val mockNotifier = mock[Notifier]
|
||||||
|
private val mockNotifiers = AllNotifiers(List(mockNotifier))
|
||||||
|
|
||||||
doReturn(IO.pure(Some(okZone))).when(mockZoneRepo).getZone(okZone.id)
|
doReturn(IO.pure(Some(okZone))).when(mockZoneRepo).getZone(okZone.id)
|
||||||
doReturn(IO.pure(Some(zoneNotAuthorized)))
|
doReturn(IO.pure(Some(zoneNotAuthorized)))
|
||||||
@ -85,7 +90,8 @@ class RecordSetServiceSpec
|
|||||||
VinylDNSTestHelpers.highValueDomainConfig,
|
VinylDNSTestHelpers.highValueDomainConfig,
|
||||||
VinylDNSTestHelpers.dottedHostsConfig,
|
VinylDNSTestHelpers.dottedHostsConfig,
|
||||||
VinylDNSTestHelpers.approvedNameServers,
|
VinylDNSTestHelpers.approvedNameServers,
|
||||||
true
|
true,
|
||||||
|
mockNotifiers
|
||||||
)
|
)
|
||||||
|
|
||||||
val underTestWithDnsBackendValidations = new RecordSetService(
|
val underTestWithDnsBackendValidations = new RecordSetService(
|
||||||
@ -104,7 +110,8 @@ class RecordSetServiceSpec
|
|||||||
VinylDNSTestHelpers.highValueDomainConfig,
|
VinylDNSTestHelpers.highValueDomainConfig,
|
||||||
VinylDNSTestHelpers.dottedHostsConfig,
|
VinylDNSTestHelpers.dottedHostsConfig,
|
||||||
VinylDNSTestHelpers.approvedNameServers,
|
VinylDNSTestHelpers.approvedNameServers,
|
||||||
true
|
true,
|
||||||
|
mockNotifiers
|
||||||
)
|
)
|
||||||
|
|
||||||
val underTestWithEmptyDottedHostsConfig = new RecordSetService(
|
val underTestWithEmptyDottedHostsConfig = new RecordSetService(
|
||||||
@ -123,7 +130,8 @@ class RecordSetServiceSpec
|
|||||||
VinylDNSTestHelpers.highValueDomainConfig,
|
VinylDNSTestHelpers.highValueDomainConfig,
|
||||||
VinylDNSTestHelpers.emptyDottedHostsConfig,
|
VinylDNSTestHelpers.emptyDottedHostsConfig,
|
||||||
VinylDNSTestHelpers.approvedNameServers,
|
VinylDNSTestHelpers.approvedNameServers,
|
||||||
true
|
true,
|
||||||
|
mockNotifiers
|
||||||
)
|
)
|
||||||
|
|
||||||
def getDottedHostsConfigGroupsAllowed(zone: Zone, config: DottedHostsConfig): List[String] = {
|
def getDottedHostsConfigGroupsAllowed(zone: Zone, config: DottedHostsConfig): List[String] = {
|
||||||
@ -1177,6 +1185,9 @@ class RecordSetServiceSpec
|
|||||||
doReturn(IO.pure(Some(oldRecord)))
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
.when(mockRecordRepo)
|
.when(mockRecordRepo)
|
||||||
.getRecordSet(oldRecord.id)
|
.getRecordSet(oldRecord.id)
|
||||||
|
doReturn(IO.pure(Some(newRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
doReturn(IO.pure(List(oldRecord)))
|
doReturn(IO.pure(List(oldRecord)))
|
||||||
.when(mockRecordRepo)
|
.when(mockRecordRepo)
|
||||||
.getRecordSetsByName(zone.id, oldRecord.name)
|
.getRecordSetsByName(zone.id, oldRecord.name)
|
||||||
@ -1185,7 +1196,7 @@ class RecordSetServiceSpec
|
|||||||
.getGroup(okGroup.id)
|
.getGroup(okGroup.id)
|
||||||
|
|
||||||
val result = underTest.updateRecordSet(newRecord, auth).value.unsafeRunSync().swap.toOption.get
|
val result = underTest.updateRecordSet(newRecord, auth).value.unsafeRunSync().swap.toOption.get
|
||||||
result shouldBe an[InvalidRequest]
|
result shouldBe an[NotAuthorizedError]
|
||||||
}
|
}
|
||||||
"succeed if user is in owner group and zone is shared" in {
|
"succeed if user is in owner group and zone is shared" in {
|
||||||
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
@ -1314,6 +1325,9 @@ class RecordSetServiceSpec
|
|||||||
doReturn(IO.pure(Some(oldRecord)))
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
.when(mockRecordRepo)
|
.when(mockRecordRepo)
|
||||||
.getRecordSet(newRecord.id)
|
.getRecordSet(newRecord.id)
|
||||||
|
doReturn(IO.pure(Some(newRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
doReturn(IO.pure(List(oldRecord)))
|
doReturn(IO.pure(List(oldRecord)))
|
||||||
.when(mockRecordRepo)
|
.when(mockRecordRepo)
|
||||||
.getRecordSetsByName(zone.id, newRecord.name)
|
.getRecordSetsByName(zone.id, newRecord.name)
|
||||||
@ -2200,4 +2214,514 @@ class RecordSetServiceSpec
|
|||||||
"thing.com."
|
"thing.com."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"ownerShipTransfer" should {
|
||||||
|
"success if user request AutoApproved for the owner group is null" in {
|
||||||
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
|
val auth = AuthPrincipal(listOfDummyUsers.head, Seq(oneUserDummyGroup.id))
|
||||||
|
val oldRecord = aaaa.copy(
|
||||||
|
name = "test-owner-group-success",
|
||||||
|
zoneId = zone.id,
|
||||||
|
status = RecordSetStatus.Active,
|
||||||
|
ownerGroupId = None
|
||||||
|
)
|
||||||
|
|
||||||
|
val newRecord = oldRecord.copy(recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.AutoApproved,requestedOwnerGroupId = Some(okGroup.id))))
|
||||||
|
|
||||||
|
doReturn(IO.pure(Some(zone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(zone.id)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
doReturn(IO.pure(List(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByName(zone.id, newRecord.name)
|
||||||
|
doReturn(IO.pure(Some(oneUserDummyGroup)))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroup(oneUserDummyGroup.id)
|
||||||
|
doReturn(IO.pure(Some(okGroup)))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroup(okGroup.id)
|
||||||
|
doReturn(IO.pure(Set(dottedZone, abcZone, xyzZone, dotZone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByNames(dottedHostsConfigZonesAllowed.toSet)
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByFilters(Set.empty)
|
||||||
|
doReturn(IO.pure(None))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZoneByName(newRecord.name + "." + okZone.name)
|
||||||
|
doReturn(IO.pure(List()))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByFQDNs(Set(newRecord.name + "." + okZone.name))
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByFilters(Set.empty)
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroupsByName(dottedHostsConfigGroupsAllowed.toSet)
|
||||||
|
doReturn(IO.pure(ListUsersResults(Seq(), None)))
|
||||||
|
.when(mockUserRepo)
|
||||||
|
.getUsers(Set.empty, None, None)
|
||||||
|
|
||||||
|
val result =
|
||||||
|
underTest.updateRecordSet(newRecord, auth).map(_.asInstanceOf[RecordSetChange]).value.unsafeRunSync().toOption.get
|
||||||
|
|
||||||
|
result.recordSet.ownerGroupId shouldBe Some(okGroup.id)
|
||||||
|
result.recordSet.recordSetGroupChange.map(_.requestedOwnerGroupId.get) shouldBe Some(okGroup.id)
|
||||||
|
result.recordSet.recordSetGroupChange.map(_.ownerShipTransferStatus) shouldBe Some(OwnerShipTransferStatus.AutoApproved)
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail if user request ownership transfer for non shared zone" in {
|
||||||
|
val zone = okZone.copy(id = "test-owner-group")
|
||||||
|
val oldRecord = aaaa.copy(
|
||||||
|
name = "test-owner-group-failure",
|
||||||
|
zoneId = zone.id,
|
||||||
|
status = RecordSetStatus.Active,
|
||||||
|
ownerGroupId = Some(oneUserDummyGroup.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
val newRecord = oldRecord.copy(recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.Requested, requestedOwnerGroupId = Some(okGroup.id))))
|
||||||
|
doReturn(IO.pure(Some(zone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(zone.id)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
doReturn(IO.pure(List(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByName(zone.id, oldRecord.name)
|
||||||
|
|
||||||
|
val result = underTest.updateRecordSet(newRecord, okAuth).value.unsafeRunSync().swap.toOption.get
|
||||||
|
result shouldBe an[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail if user not a member of owner group and tried to Approve ownership transfer request" in {
|
||||||
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
|
val oldRecord = aaaa.copy(
|
||||||
|
name = "test-owner-group-failure",
|
||||||
|
zoneId = zone.id,
|
||||||
|
status = RecordSetStatus.Active,
|
||||||
|
ownerGroupId = Some(oneUserDummyGroup.id),
|
||||||
|
recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.PendingReview,requestedOwnerGroupId = Some(okGroup.id)))
|
||||||
|
)
|
||||||
|
|
||||||
|
val newRecord = oldRecord.copy(recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyApproved)))
|
||||||
|
doReturn(IO.pure(Some(zone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(zone.id)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
doReturn(IO.pure(List(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByName(zone.id, oldRecord.name)
|
||||||
|
doReturn(IO.pure(Some(okGroup)))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroup(okGroup.id)
|
||||||
|
|
||||||
|
val result = underTest.updateRecordSet(newRecord, okAuth).value.unsafeRunSync().swap.toOption.get
|
||||||
|
result shouldBe an[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail if user not a member of owner group and tried to Reject ownership transfer request" in {
|
||||||
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
|
val oldRecord = aaaa.copy(
|
||||||
|
name = "test-owner-group-failure",
|
||||||
|
zoneId = zone.id,
|
||||||
|
status = RecordSetStatus.Active,
|
||||||
|
ownerGroupId = Some(oneUserDummyGroup.id),
|
||||||
|
recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.PendingReview,requestedOwnerGroupId = Some(okGroup.id)))
|
||||||
|
)
|
||||||
|
|
||||||
|
val newRecord = oldRecord.copy(recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyRejected)))
|
||||||
|
doReturn(IO.pure(Some(zone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(zone.id)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
doReturn(IO.pure(List(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByName(zone.id, oldRecord.name)
|
||||||
|
doReturn(IO.pure(Some(okGroup)))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroup(okGroup.id)
|
||||||
|
|
||||||
|
val result = underTest.updateRecordSet(newRecord, okAuth).value.unsafeRunSync().swap.toOption.get
|
||||||
|
result shouldBe an[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"success if user not a member of owner group and tried to Request ownership transfer request" in {
|
||||||
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
|
val oldRecord = aaaa.copy(
|
||||||
|
name = "test-owner-group-success",
|
||||||
|
zoneId = zone.id,
|
||||||
|
status = RecordSetStatus.Active,
|
||||||
|
ownerGroupId = Some(oneUserDummyGroup.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
val newRecord = oldRecord.copy(recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.Requested,requestedOwnerGroupId = Some(okGroup.id))))
|
||||||
|
|
||||||
|
doReturn(IO.pure(Some(zone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(zone.id)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
doReturn(IO.pure(List(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByName(zone.id, newRecord.name)
|
||||||
|
doReturn(IO.pure(Some(oneUserDummyGroup)))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroup(oneUserDummyGroup.id)
|
||||||
|
doReturn(IO.pure(Some(okGroup)))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroup(okGroup.id)
|
||||||
|
doReturn(IO.pure(Set(dottedZone, abcZone, xyzZone, dotZone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByNames(dottedHostsConfigZonesAllowed.toSet)
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByFilters(Set.empty)
|
||||||
|
doReturn(IO.pure(None))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZoneByName(newRecord.name + "." + okZone.name)
|
||||||
|
doReturn(IO.pure(List()))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByFQDNs(Set(newRecord.name + "." + okZone.name))
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByFilters(Set.empty)
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroupsByName(dottedHostsConfigGroupsAllowed.toSet)
|
||||||
|
doReturn(IO.pure(ListUsersResults(Seq(), None)))
|
||||||
|
.when(mockUserRepo)
|
||||||
|
.getUsers(Set.empty, None, None)
|
||||||
|
doReturn(IO.unit).when(mockNotifier).notify(any[Notification[_]])
|
||||||
|
|
||||||
|
val result =
|
||||||
|
underTest.updateRecordSet(newRecord, okAuth).map(_.asInstanceOf[RecordSetChange]).value.unsafeRunSync().toOption.get
|
||||||
|
|
||||||
|
result.recordSet.ownerGroupId shouldBe Some(oneUserDummyGroup.id)
|
||||||
|
result.recordSet.recordSetGroupChange.map(_.requestedOwnerGroupId.get) shouldBe Some(okGroup.id)
|
||||||
|
result.recordSet.recordSetGroupChange.map(_.ownerShipTransferStatus) shouldBe Some(OwnerShipTransferStatus.PendingReview)
|
||||||
|
}
|
||||||
|
|
||||||
|
"success if user not a member of owner group and tried to Cancel ownership transfer request" in {
|
||||||
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
|
val oldRecord = aaaa.copy(
|
||||||
|
name = "test-owner-group-success",
|
||||||
|
zoneId = zone.id,
|
||||||
|
status = RecordSetStatus.Active,
|
||||||
|
ownerGroupId = Some(oneUserDummyGroup.id),
|
||||||
|
recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.PendingReview, requestedOwnerGroupId = Some(okGroup.id)))
|
||||||
|
)
|
||||||
|
|
||||||
|
val newRecord = oldRecord.copy(recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.Cancelled)))
|
||||||
|
|
||||||
|
doReturn(IO.pure(Some(zone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(zone.id)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
doReturn(IO.pure(List(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByName(zone.id, newRecord.name)
|
||||||
|
doReturn(IO.pure(Some(oneUserDummyGroup)))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroup(oneUserDummyGroup.id)
|
||||||
|
doReturn(IO.pure(Some(okGroup)))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroup(okGroup.id)
|
||||||
|
doReturn(IO.pure(Set(dottedZone, abcZone, xyzZone, dotZone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByNames(dottedHostsConfigZonesAllowed.toSet)
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByFilters(Set.empty)
|
||||||
|
doReturn(IO.pure(None))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZoneByName(newRecord.name + "." + okZone.name)
|
||||||
|
doReturn(IO.pure(List()))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByFQDNs(Set(newRecord.name + "." + okZone.name))
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByFilters(Set.empty)
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroupsByName(dottedHostsConfigGroupsAllowed.toSet)
|
||||||
|
doReturn(IO.pure(ListUsersResults(Seq(), None)))
|
||||||
|
.when(mockUserRepo)
|
||||||
|
.getUsers(Set.empty, None, None)
|
||||||
|
doReturn(IO.unit).when(mockNotifier).notify(any[Notification[_]])
|
||||||
|
|
||||||
|
val result =
|
||||||
|
underTest.updateRecordSet(newRecord, okAuth).map(_.asInstanceOf[RecordSetChange]).value.unsafeRunSync().toOption.get
|
||||||
|
|
||||||
|
result.recordSet.ownerGroupId shouldBe Some(oneUserDummyGroup.id)
|
||||||
|
result.recordSet.recordSetGroupChange.map(_.requestedOwnerGroupId.get) shouldBe Some(okGroup.id)
|
||||||
|
result.recordSet.recordSetGroupChange.map(_.ownerShipTransferStatus) shouldBe Some(OwnerShipTransferStatus.Cancelled)
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail if user not a member of owner group and tried to update ttl while requesting ownership transfer" in {
|
||||||
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
|
val oldRecord =
|
||||||
|
aaaa.copy(zoneId = zone.id, status = RecordSetStatus.Active, ownerGroupId = Some(oneUserDummyGroup.id))
|
||||||
|
val newRecord = oldRecord.copy(
|
||||||
|
ttl = oldRecord.ttl + 1000,
|
||||||
|
recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.Requested,requestedOwnerGroupId = Some(okGroup.id))))
|
||||||
|
|
||||||
|
doReturn(IO.pure(Some(okZone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(newRecord.zoneId)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
|
||||||
|
val result = underTest.updateRecordSet(newRecord, okAuth).value.unsafeRunSync().swap.toOption.get
|
||||||
|
result shouldBe a[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail if user not a member of owner group and tried to update ttl while cancel ownership transfer" in {
|
||||||
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
|
val oldRecord =
|
||||||
|
aaaa.copy(zoneId = zone.id, status = RecordSetStatus.Active, ownerGroupId = Some(oneUserDummyGroup.id),recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.PendingReview, requestedOwnerGroupId = Some(okGroup.id)))
|
||||||
|
)
|
||||||
|
val newRecord = oldRecord.copy(
|
||||||
|
records = List(AAAAData("1:2:3:4:5:6:7:9")),
|
||||||
|
recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.Cancelled)))
|
||||||
|
|
||||||
|
doReturn(IO.pure(Some(okZone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(newRecord.zoneId)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
|
||||||
|
val result = underTest.updateRecordSet(newRecord, okAuth).value.unsafeRunSync().swap.toOption.get
|
||||||
|
result shouldBe a[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail if user cancelled the ownership request and group member tried to approve the request" in {
|
||||||
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
|
val auth = AuthPrincipal(listOfDummyUsers.head, Seq(oneUserDummyGroup.id))
|
||||||
|
|
||||||
|
val oldRecord = aaaa.copy(
|
||||||
|
name = "test-owner-group-failure",
|
||||||
|
zoneId = zone.id,
|
||||||
|
status = RecordSetStatus.Active,
|
||||||
|
ownerGroupId = Some(oneUserDummyGroup.id),
|
||||||
|
recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.Cancelled,requestedOwnerGroupId = Some(okGroup.id)))
|
||||||
|
)
|
||||||
|
|
||||||
|
val newRecord = oldRecord.copy(recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyApproved)))
|
||||||
|
|
||||||
|
doReturn(IO.pure(Some(okZone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(newRecord.zoneId)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
|
||||||
|
val result = underTest.updateRecordSet(newRecord, auth).value.unsafeRunSync().swap.toOption.get
|
||||||
|
result shouldBe a[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail if user cancelled the ownership request and group member tried to reject the request" in {
|
||||||
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
|
val auth = AuthPrincipal(listOfDummyUsers.head, Seq(oneUserDummyGroup.id))
|
||||||
|
|
||||||
|
val oldRecord = aaaa.copy(
|
||||||
|
name = "test-owner-group-failure",
|
||||||
|
zoneId = zone.id,
|
||||||
|
status = RecordSetStatus.Active,
|
||||||
|
ownerGroupId = Some(oneUserDummyGroup.id),
|
||||||
|
recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.Cancelled,requestedOwnerGroupId = Some(okGroup.id)))
|
||||||
|
)
|
||||||
|
|
||||||
|
val newRecord = oldRecord.copy(recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyRejected)))
|
||||||
|
|
||||||
|
doReturn(IO.pure(Some(okZone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(newRecord.zoneId)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
|
||||||
|
val result = underTest.updateRecordSet(newRecord, auth).value.unsafeRunSync().swap.toOption.get
|
||||||
|
result shouldBe a[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail if user cancelled the ownership request and group member tried to reject the auto-approve" in {
|
||||||
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
|
val auth = AuthPrincipal(listOfDummyUsers.head, Seq(oneUserDummyGroup.id))
|
||||||
|
|
||||||
|
val oldRecord = aaaa.copy(
|
||||||
|
name = "test-owner-group-failure",
|
||||||
|
zoneId = zone.id,
|
||||||
|
status = RecordSetStatus.Active,
|
||||||
|
ownerGroupId = Some(oneUserDummyGroup.id),
|
||||||
|
recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.Cancelled,requestedOwnerGroupId = Some(okGroup.id)))
|
||||||
|
)
|
||||||
|
|
||||||
|
val newRecord = oldRecord.copy(recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.AutoApproved)))
|
||||||
|
|
||||||
|
doReturn(IO.pure(Some(okZone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(newRecord.zoneId)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
|
||||||
|
val result = underTest.updateRecordSet(newRecord, auth).value.unsafeRunSync().swap.toOption.get
|
||||||
|
result shouldBe a[InvalidRequest]
|
||||||
|
}
|
||||||
|
|
||||||
|
"success if user Approve a ownership transfer request Manually" in {
|
||||||
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
|
val auth = AuthPrincipal(listOfDummyUsers.head, Seq(oneUserDummyGroup.id))
|
||||||
|
val oldRecord = aaaa.copy(
|
||||||
|
name = "test-owner-group-success",
|
||||||
|
zoneId = zone.id,
|
||||||
|
status = RecordSetStatus.Active,
|
||||||
|
ownerGroupId = Some(oneUserDummyGroup.id),
|
||||||
|
recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.PendingReview,requestedOwnerGroupId = Some(okGroup.id)))
|
||||||
|
)
|
||||||
|
|
||||||
|
val newRecord = oldRecord.copy(recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyApproved)))
|
||||||
|
|
||||||
|
doReturn(IO.pure(Some(zone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(zone.id)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
doReturn(IO.pure(List(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByName(zone.id, newRecord.name)
|
||||||
|
doReturn(IO.pure(Some(oneUserDummyGroup)))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroup(oneUserDummyGroup.id)
|
||||||
|
doReturn(IO.pure(Some(okGroup)))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroup(okGroup.id)
|
||||||
|
doReturn(IO.pure(Set(dottedZone, abcZone, xyzZone, dotZone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByNames(dottedHostsConfigZonesAllowed.toSet)
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByFilters(Set.empty)
|
||||||
|
doReturn(IO.pure(None))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZoneByName(newRecord.name + "." + okZone.name)
|
||||||
|
doReturn(IO.pure(List()))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByFQDNs(Set(newRecord.name + "." + okZone.name))
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByFilters(Set.empty)
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroupsByName(dottedHostsConfigGroupsAllowed.toSet)
|
||||||
|
doReturn(IO.pure(ListUsersResults(Seq(), None)))
|
||||||
|
.when(mockUserRepo)
|
||||||
|
.getUsers(Set.empty, None, None)
|
||||||
|
doReturn(IO.unit).when(mockNotifier).notify(any[Notification[_]])
|
||||||
|
|
||||||
|
val result = {
|
||||||
|
underTest.updateRecordSet(newRecord, auth).map(_.asInstanceOf[RecordSetChange]).value.unsafeRunSync().toOption.get
|
||||||
|
}
|
||||||
|
|
||||||
|
result.recordSet.ownerGroupId shouldBe Some(okGroup.id)
|
||||||
|
result.recordSet.recordSetGroupChange.map(_.requestedOwnerGroupId.get) shouldBe Some(okGroup.id)
|
||||||
|
result.recordSet.recordSetGroupChange.map(_.ownerShipTransferStatus) shouldBe Some(OwnerShipTransferStatus.ManuallyApproved)
|
||||||
|
}
|
||||||
|
|
||||||
|
"success if user Reject a ownership transfer request Manually" in {
|
||||||
|
val zone = okZone.copy(shared = true, id = "test-owner-group")
|
||||||
|
val auth = AuthPrincipal(listOfDummyUsers.head, Seq(oneUserDummyGroup.id))
|
||||||
|
val oldRecord = aaaa.copy(
|
||||||
|
name = "test-owner-group-success",
|
||||||
|
zoneId = zone.id,
|
||||||
|
status = RecordSetStatus.Active,
|
||||||
|
ownerGroupId = Some(oneUserDummyGroup.id),
|
||||||
|
recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.PendingReview,requestedOwnerGroupId = Some(okGroup.id)))
|
||||||
|
)
|
||||||
|
|
||||||
|
val newRecord = oldRecord.copy(recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyRejected)))
|
||||||
|
|
||||||
|
doReturn(IO.pure(Some(zone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZone(zone.id)
|
||||||
|
doReturn(IO.pure(Some(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSet(newRecord.id)
|
||||||
|
doReturn(IO.pure(List(oldRecord)))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByName(zone.id, newRecord.name)
|
||||||
|
doReturn(IO.pure(Some(oneUserDummyGroup)))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroup(oneUserDummyGroup.id)
|
||||||
|
doReturn(IO.pure(Some(okGroup)))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroup(okGroup.id)
|
||||||
|
doReturn(IO.pure(Set(dottedZone, abcZone, xyzZone, dotZone)))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByNames(dottedHostsConfigZonesAllowed.toSet)
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByFilters(Set.empty)
|
||||||
|
doReturn(IO.pure(None))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZoneByName(newRecord.name + "." + okZone.name)
|
||||||
|
doReturn(IO.pure(List()))
|
||||||
|
.when(mockRecordRepo)
|
||||||
|
.getRecordSetsByFQDNs(Set(newRecord.name + "." + okZone.name))
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockZoneRepo)
|
||||||
|
.getZonesByFilters(Set.empty)
|
||||||
|
doReturn(IO.pure(Set()))
|
||||||
|
.when(mockGroupRepo)
|
||||||
|
.getGroupsByName(dottedHostsConfigGroupsAllowed.toSet)
|
||||||
|
doReturn(IO.pure(ListUsersResults(Seq(), None)))
|
||||||
|
.when(mockUserRepo)
|
||||||
|
.getUsers(Set.empty, None, None)
|
||||||
|
doReturn(IO.unit).when(mockNotifier).notify(any[Notification[_]])
|
||||||
|
|
||||||
|
val result = {
|
||||||
|
underTest.updateRecordSet(newRecord, auth).map(_.asInstanceOf[RecordSetChange]).value.unsafeRunSync().toOption.get
|
||||||
|
}
|
||||||
|
|
||||||
|
result.recordSet.ownerGroupId shouldBe Some(oneUserDummyGroup.id)
|
||||||
|
result.recordSet.recordSetGroupChange.map(_.requestedOwnerGroupId.get) shouldBe Some(okGroup.id)
|
||||||
|
result.recordSet.recordSetGroupChange.map(_.ownerShipTransferStatus) shouldBe Some(OwnerShipTransferStatus.ManuallyRejected)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,5 +722,85 @@ class RecordSetValidationsSpec
|
|||||||
canSuperUserUpdateOwnerGroup(existing, rs, zone, superUserAuth) should be(false)
|
canSuperUserUpdateOwnerGroup(existing, rs, zone, superUserAuth) should be(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"unchangedRecordSet" should {
|
||||||
|
"return invalid request when given zone ID does not match existing recordset zone ID" in {
|
||||||
|
val existing = rsOk
|
||||||
|
val rs = rsOk.copy(zoneId = "not-real")
|
||||||
|
val error = leftValue(unchangedRecordSet(existing, rs))
|
||||||
|
error shouldBe an[InvalidRequest]
|
||||||
|
error.getMessage() shouldBe "Cannot update RecordSet's if user not a member of ownership group. User can only request for ownership transfer"
|
||||||
|
}
|
||||||
|
"return invalid request when given record type does not match existing recordset record type" in {
|
||||||
|
val existing = rsOk
|
||||||
|
val rs = rsOk.copy(typ = RecordType.AAAA)
|
||||||
|
val error = leftValue(unchangedRecordSet(existing, rs))
|
||||||
|
error shouldBe an[InvalidRequest]
|
||||||
|
error.getMessage() shouldBe "Cannot update RecordSet's if user not a member of ownership group. User can only request for ownership transfer"
|
||||||
|
}
|
||||||
|
"return invalid request when given records does not match existing recordset records" in {
|
||||||
|
val existing = rsOk
|
||||||
|
val rs = rsOk.copy(records = List(AData("10.1.1.0")))
|
||||||
|
val error = leftValue(unchangedRecordSet(existing, rs))
|
||||||
|
error shouldBe an[InvalidRequest]
|
||||||
|
error.getMessage() shouldBe "Cannot update RecordSet's if user not a member of ownership group. User can only request for ownership transfer"
|
||||||
|
}
|
||||||
|
"return invalid request when given recordset id does not match existing recordset ID" in {
|
||||||
|
val existing = rsOk
|
||||||
|
val rs = rsOk.copy(id = abcRecord.id)
|
||||||
|
val error = leftValue(unchangedRecordSet(existing, rs))
|
||||||
|
error shouldBe an[InvalidRequest]
|
||||||
|
error.getMessage() shouldBe "Cannot update RecordSet's if user not a member of ownership group. User can only request for ownership transfer"
|
||||||
|
}
|
||||||
|
"return invalid request when given recordset name does not match existing recordset name" in {
|
||||||
|
val existing = rsOk
|
||||||
|
val rs = rsOk.copy(name = "abc")
|
||||||
|
val error = leftValue(unchangedRecordSet(existing, rs))
|
||||||
|
error shouldBe an[InvalidRequest]
|
||||||
|
error.getMessage() shouldBe "Cannot update RecordSet's if user not a member of ownership group. User can only request for ownership transfer"
|
||||||
|
}
|
||||||
|
"return invalid request when given owner group ID does not match existing recordset owner group ID" in {
|
||||||
|
val existing = rsOk
|
||||||
|
val rs = rsOk.copy(ownerGroupId = Some(abcGroup.id))
|
||||||
|
val error = leftValue(unchangedRecordSet(existing, rs))
|
||||||
|
error shouldBe an[InvalidRequest]
|
||||||
|
error.getMessage() shouldBe "Cannot update RecordSet's if user not a member of ownership group. User can only request for ownership transfer"
|
||||||
|
}
|
||||||
|
"return invalid request when given ttl does not match existing recordset ttl" in {
|
||||||
|
val existing = rsOk
|
||||||
|
val rs = rsOk.copy(ttl = 3000)
|
||||||
|
val error = leftValue(unchangedRecordSet(existing, rs))
|
||||||
|
error shouldBe an[InvalidRequest]
|
||||||
|
error.getMessage() shouldBe "Cannot update RecordSet's if user not a member of ownership group. User can only request for ownership transfer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"recordSetOwnerShipApproveStatus" should {
|
||||||
|
"return invalid request when given ownership transfer does not match OwnerShipTransferStatus as ManuallyRejected" in {
|
||||||
|
val rs = rsOk.copy(recordSetGroupChange = Some(ownerShipTransfer.copy(OwnerShipTransferStatus.ManuallyRejected)))
|
||||||
|
val error = leftValue(recordSetOwnerShipApproveStatus(rs))
|
||||||
|
error shouldBe an[InvalidRequest]
|
||||||
|
error.getMessage() shouldBe "Cannot update RecordSet OwnerShip Status when request is cancelled."
|
||||||
|
}
|
||||||
|
"return invalid request when given ownership transfer does not match OwnerShipTransferStatus as ManuallyApproved" in {
|
||||||
|
val rs = rsOk.copy(recordSetGroupChange = Some(ownerShipTransfer.copy(OwnerShipTransferStatus.ManuallyApproved)))
|
||||||
|
val error = leftValue(recordSetOwnerShipApproveStatus(rs))
|
||||||
|
error shouldBe an[InvalidRequest]
|
||||||
|
error.getMessage() shouldBe "Cannot update RecordSet OwnerShip Status when request is cancelled."
|
||||||
|
}
|
||||||
|
"return invalid request when given ownership transfer does not match OwnerShipTransferStatus as AutoApproved" in {
|
||||||
|
val rs = rsOk.copy(recordSetGroupChange = Some(ownerShipTransfer.copy(OwnerShipTransferStatus.AutoApproved)))
|
||||||
|
val error = leftValue(recordSetOwnerShipApproveStatus(rs))
|
||||||
|
error shouldBe an[InvalidRequest]
|
||||||
|
error.getMessage() shouldBe "Cannot update RecordSet OwnerShip Status when request is cancelled."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"unchangedRecordSetOwnershipStatus" should {
|
||||||
|
"return invalid request when given ownership transfer status does not match existing recordset ownership transfer status for non shared zones" in {
|
||||||
|
val existing = rsOk
|
||||||
|
val rs = rsOk.copy(recordSetGroupChange = Some(ownerShipTransfer.copy(OwnerShipTransferStatus.AutoApproved)))
|
||||||
|
val error = leftValue(unchangedRecordSetOwnershipStatus(existing, rs))
|
||||||
|
error shouldBe an[InvalidRequest]
|
||||||
|
error.getMessage() shouldBe "Cannot update RecordSet OwnerShip Status when zone is not shared."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,10 @@ import org.scalatest.wordspec.AnyWordSpec
|
|||||||
import org.scalatest.BeforeAndAfterEach
|
import org.scalatest.BeforeAndAfterEach
|
||||||
import org.scalatestplus.mockito.MockitoSugar
|
import org.scalatestplus.mockito.MockitoSugar
|
||||||
import vinyldns.api.CatsHelpers
|
import vinyldns.api.CatsHelpers
|
||||||
|
|
||||||
import javax.mail.{Provider, Session, Transport, URLName}
|
import javax.mail.{Provider, Session, Transport, URLName}
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
import vinyldns.core.domain.membership.{User, UserRepository}
|
import vinyldns.core.domain.membership.{GroupRepository, User, UserRepository}
|
||||||
import vinyldns.core.notifier.Notification
|
import vinyldns.core.notifier.Notification
|
||||||
|
|
||||||
import javax.mail.internet.InternetAddress
|
import javax.mail.internet.InternetAddress
|
||||||
@ -30,18 +31,22 @@ import org.mockito.Matchers.{eq => eqArg, _}
|
|||||||
import org.mockito.Mockito._
|
import org.mockito.Mockito._
|
||||||
import org.mockito.ArgumentCaptor
|
import org.mockito.ArgumentCaptor
|
||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
|
|
||||||
import javax.mail.{Address, Message}
|
import javax.mail.{Address, Message}
|
||||||
import _root_.vinyldns.core.domain.batch._
|
import _root_.vinyldns.core.domain.batch._
|
||||||
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import vinyldns.core.domain.record.RecordType
|
import vinyldns.core.domain.record.{AData, OwnerShipTransferStatus, RecordSetChange, RecordSetChangeStatus, RecordSetChangeType, RecordType}
|
||||||
import vinyldns.core.domain.record.AData
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import vinyldns.core.domain.Encrypted
|
import vinyldns.core.domain.Encrypted
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import vinyldns.core.notifier.NotifierConfig
|
import vinyldns.core.notifier.NotifierConfig
|
||||||
|
import vinyldns.core.TestMembershipData.{dummyGroup, dummyUser, okGroup, okUser}
|
||||||
|
import vinyldns.core.TestRecordSetData.{ownerShipTransfer, rsOk}
|
||||||
|
import vinyldns.core.TestZoneData.okZone
|
||||||
|
|
||||||
object MockTransport extends MockitoSugar {
|
object MockTransport extends MockitoSugar {
|
||||||
val mockTransport: Transport = mock[Transport]
|
val mockTransport: Transport = mock[Transport]
|
||||||
@ -68,6 +73,7 @@ class EmailNotifierSpec
|
|||||||
import MockTransport._
|
import MockTransport._
|
||||||
|
|
||||||
val mockUserRepository: UserRepository = mock[UserRepository]
|
val mockUserRepository: UserRepository = mock[UserRepository]
|
||||||
|
val mockGroupRepository: GroupRepository = mock[GroupRepository]
|
||||||
val session: Session = Session.getInstance(new Properties())
|
val session: Session = Session.getInstance(new Properties())
|
||||||
session.setProvider(
|
session.setProvider(
|
||||||
new Provider(
|
new Provider(
|
||||||
@ -100,6 +106,17 @@ class EmailNotifierSpec
|
|||||||
"testBatch"
|
"testBatch"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def reccordSetChange: RecordSetChange =
|
||||||
|
RecordSetChange(
|
||||||
|
okZone,
|
||||||
|
rsOk.copy(ownerGroupId= Some(okGroup.id),recordSetGroupChange =
|
||||||
|
Some(ownerShipTransfer.copy(ownerShipTransferStatus = OwnerShipTransferStatus.PendingReview, requestedOwnerGroupId = Some(dummyGroup.id)))),
|
||||||
|
"system",
|
||||||
|
RecordSetChangeType.Create,
|
||||||
|
RecordSetChangeStatus.Complete
|
||||||
|
)
|
||||||
|
|
||||||
"Email Notifier" should {
|
"Email Notifier" should {
|
||||||
"do nothing for unsupported Notifications" in {
|
"do nothing for unsupported Notifications" in {
|
||||||
val emailConfig: Config = ConfigFactory.parseMap(
|
val emailConfig: Config = ConfigFactory.parseMap(
|
||||||
@ -110,7 +127,7 @@ class EmailNotifierSpec
|
|||||||
).asJava
|
).asJava
|
||||||
)
|
)
|
||||||
val notifier = new EmailNotifierProvider()
|
val notifier = new EmailNotifierProvider()
|
||||||
.load(NotifierConfig("", emailConfig), mockUserRepository)
|
.load(NotifierConfig("", emailConfig), mockUserRepository, mockGroupRepository)
|
||||||
.unsafeRunSync()
|
.unsafeRunSync()
|
||||||
|
|
||||||
notifier.notify(new Notification("this won't be supported ever")) should be(IO.unit)
|
notifier.notify(new Notification("this won't be supported ever")) should be(IO.unit)
|
||||||
@ -120,7 +137,8 @@ class EmailNotifierSpec
|
|||||||
val notifier = new EmailNotifier(
|
val notifier = new EmailNotifier(
|
||||||
EmailNotifierConfig(new InternetAddress("test@test.com"), new Properties()),
|
EmailNotifierConfig(new InternetAddress("test@test.com"), new Properties()),
|
||||||
session,
|
session,
|
||||||
mockUserRepository
|
mockUserRepository,
|
||||||
|
mockGroupRepository
|
||||||
)
|
)
|
||||||
|
|
||||||
doReturn(IO.pure(Some(User("testUser", "access", Encrypted("secret")))))
|
doReturn(IO.pure(Some(User("testUser", "access", Encrypted("secret")))))
|
||||||
@ -132,11 +150,60 @@ class EmailNotifierSpec
|
|||||||
verify(mockUserRepository).getUser("test")
|
verify(mockUserRepository).getUser("test")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"send an email to a user for recordSet ownership transfer" in {
|
||||||
|
val fromAddress = new InternetAddress("test@test.com")
|
||||||
|
|
||||||
|
val notifier = new EmailNotifier(
|
||||||
|
EmailNotifierConfig(fromAddress, new Properties()),
|
||||||
|
session,
|
||||||
|
mockUserRepository,
|
||||||
|
mockGroupRepository
|
||||||
|
)
|
||||||
|
val expectedAddresses = Array[Address](new InternetAddress("test@test.com"),new InternetAddress("test@test.com"))
|
||||||
|
val messageArgument = ArgumentCaptor.forClass(classOf[Message])
|
||||||
|
val dummyGrp = dummyGroup.copy(memberIds = Set(dummyUser.id))
|
||||||
|
val dummyUsr = dummyUser.copy(id=dummyUser.id,email = Some("test@test.com"))
|
||||||
|
|
||||||
|
doNothing().when(mockTransport).connect()
|
||||||
|
doNothing()
|
||||||
|
.when(mockTransport)
|
||||||
|
.sendMessage(messageArgument.capture(), eqArg(expectedAddresses))
|
||||||
|
doNothing().when(mockTransport).close()
|
||||||
|
doReturn(IO.pure(Some(okGroup)))
|
||||||
|
.when(mockGroupRepository)
|
||||||
|
.getGroup(okGroup.id)
|
||||||
|
doReturn(IO.pure(Some(dummyGrp)))
|
||||||
|
.when(mockGroupRepository)
|
||||||
|
.getGroup(dummyGrp.id)
|
||||||
|
doReturn(IO.pure(Some(okUser)))
|
||||||
|
.when(mockUserRepository)
|
||||||
|
.getUser(okUser.id)
|
||||||
|
doReturn(IO.pure(Some(dummyUsr)))
|
||||||
|
.when(mockUserRepository)
|
||||||
|
.getUser(dummyGrp.memberIds.head)
|
||||||
|
|
||||||
|
val rsc = reccordSetChange.copy(userId = okUser.id)
|
||||||
|
|
||||||
|
notifier.notify(Notification(rsc)).unsafeRunSync()
|
||||||
|
val message = messageArgument.getValue
|
||||||
|
message.getFrom should be(Array(fromAddress))
|
||||||
|
message.getContentType should be("text/html; charset=us-ascii")
|
||||||
|
message.getAllRecipients should be(expectedAddresses)
|
||||||
|
message.getSubject should be(s"VinylDNS RecordSet change ${rsc.id} results")
|
||||||
|
|
||||||
|
val content = message.getContent.asInstanceOf[String]
|
||||||
|
|
||||||
|
content.contains(rsc.id) should be(true)
|
||||||
|
content.contains(rsc.recordSet.ownerGroupId.get) should be(true)
|
||||||
|
content.contains(rsc.recordSet.recordSetGroupChange.map(_.requestedOwnerGroupId.get).get) should be(true)
|
||||||
|
}
|
||||||
|
|
||||||
"do nothing when user not found" in {
|
"do nothing when user not found" in {
|
||||||
val notifier = new EmailNotifier(
|
val notifier = new EmailNotifier(
|
||||||
EmailNotifierConfig(new InternetAddress("test@test.com"), new Properties()),
|
EmailNotifierConfig(new InternetAddress("test@test.com"), new Properties()),
|
||||||
session,
|
session,
|
||||||
mockUserRepository
|
mockUserRepository,
|
||||||
|
mockGroupRepository
|
||||||
)
|
)
|
||||||
|
|
||||||
doReturn(IO.pure(None))
|
doReturn(IO.pure(None))
|
||||||
@ -148,12 +215,13 @@ class EmailNotifierSpec
|
|||||||
verify(mockUserRepository).getUser("test")
|
verify(mockUserRepository).getUser("test")
|
||||||
}
|
}
|
||||||
|
|
||||||
"send an email to a user" in {
|
"send an email to a user for batch change" in {
|
||||||
val fromAddress = new InternetAddress("test@test.com")
|
val fromAddress = new InternetAddress("test@test.com")
|
||||||
val notifier = new EmailNotifier(
|
val notifier = new EmailNotifier(
|
||||||
EmailNotifierConfig(fromAddress, new Properties()),
|
EmailNotifierConfig(fromAddress, new Properties()),
|
||||||
session,
|
session,
|
||||||
mockUserRepository
|
mockUserRepository,
|
||||||
|
mockGroupRepository
|
||||||
)
|
)
|
||||||
|
|
||||||
doReturn(
|
doReturn(
|
||||||
|
@ -20,6 +20,7 @@ import org.scalatest.wordspec.AnyWordSpec
|
|||||||
import org.scalatest.BeforeAndAfterEach
|
import org.scalatest.BeforeAndAfterEach
|
||||||
import org.scalatestplus.mockito.MockitoSugar
|
import org.scalatestplus.mockito.MockitoSugar
|
||||||
import vinyldns.api.CatsHelpers
|
import vinyldns.api.CatsHelpers
|
||||||
|
import vinyldns.core.domain.membership.{GroupRepository, UserRepository}
|
||||||
import vinyldns.core.domain.membership.UserRepository
|
import vinyldns.core.domain.membership.UserRepository
|
||||||
import vinyldns.core.notifier.Notification
|
import vinyldns.core.notifier.Notification
|
||||||
import org.mockito.Matchers._
|
import org.mockito.Matchers._
|
||||||
@ -51,6 +52,7 @@ class SnsNotifierSpec
|
|||||||
with CatsHelpers {
|
with CatsHelpers {
|
||||||
|
|
||||||
val mockUserRepository = mock[UserRepository]
|
val mockUserRepository = mock[UserRepository]
|
||||||
|
val mockGroupRepository = mock[GroupRepository]
|
||||||
val mockSns = mock[AmazonSNS]
|
val mockSns = mock[AmazonSNS]
|
||||||
|
|
||||||
override protected def beforeEach(): Unit =
|
override protected def beforeEach(): Unit =
|
||||||
@ -86,7 +88,7 @@ class SnsNotifierSpec
|
|||||||
).asJava
|
).asJava
|
||||||
)
|
)
|
||||||
val notifier = new SnsNotifierProvider()
|
val notifier = new SnsNotifierProvider()
|
||||||
.load(NotifierConfig("", snsConfig), mockUserRepository)
|
.load(NotifierConfig("", snsConfig), mockUserRepository, mockGroupRepository)
|
||||||
.unsafeRunSync()
|
.unsafeRunSync()
|
||||||
|
|
||||||
notifier.notify(Notification("this won't be supported ever")) should be(IO.unit)
|
notifier.notify(Notification("this won't be supported ever")) should be(IO.unit)
|
||||||
|
@ -680,6 +680,206 @@ class VinylDNSJsonProtocolSpec
|
|||||||
val thrown = the[MappingException] thrownBy recordSetJValue.extract[RecordSet]
|
val thrown = the[MappingException] thrownBy recordSetJValue.extract[RecordSet]
|
||||||
thrown.msg should include("Digest Type 0 is not a supported DS record digest type")
|
thrown.msg should include("Digest Type 0 is not a supported DS record digest type")
|
||||||
}
|
}
|
||||||
|
"auto-approve a owner ship transfer request" in {
|
||||||
|
val recordSetJValue: JValue =
|
||||||
|
("zoneId" -> "1") ~~
|
||||||
|
("name" -> "TestRecordName") ~~
|
||||||
|
("type" -> "CNAME") ~~
|
||||||
|
("ttl" -> 1000) ~~
|
||||||
|
("status" -> "Pending") ~~
|
||||||
|
("records" -> List("cname" -> "cname.data ")) ~~
|
||||||
|
("recordSetGroupChange" -> Some("ownerShipTransferStatus" -> "AutoApproved"))
|
||||||
|
|
||||||
|
|
||||||
|
val expected = RecordSet(
|
||||||
|
"1",
|
||||||
|
"TestRecordName",
|
||||||
|
RecordType.CNAME,
|
||||||
|
1000,
|
||||||
|
RecordSetStatus.Pending,
|
||||||
|
LocalDateTime.of(2010, Month.JANUARY, 1, 0, 0).toInstant(ZoneOffset.UTC),
|
||||||
|
records = List(CNAMEData(Fqdn("cname.data."))),
|
||||||
|
recordSetGroupChange = Some(OwnerShipTransfer(ownerShipTransferStatus = OwnerShipTransferStatus.AutoApproved))
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
val actual = recordSetJValue.extract[RecordSet]
|
||||||
|
anonymize(actual) shouldBe anonymize(expected)
|
||||||
|
anonymize(actual).recordSetGroupChange.get.ownerShipTransferStatus shouldBe OwnerShipTransferStatus.AutoApproved
|
||||||
|
}
|
||||||
|
|
||||||
|
"manually-approve a owner ship transfer request" in {
|
||||||
|
val recordSetJValue: JValue =
|
||||||
|
("zoneId" -> "1") ~~
|
||||||
|
("name" -> "TestRecordName") ~~
|
||||||
|
("type" -> "CNAME") ~~
|
||||||
|
("ttl" -> 1000) ~~
|
||||||
|
("status" -> "Pending") ~~
|
||||||
|
("records" -> List("cname" -> "cname.data ")) ~~
|
||||||
|
("ownerGroupId" -> "updated-admin-group-id") ~~
|
||||||
|
("recordSetGroupChange" -> Some(("ownerShipTransferStatus" -> "ManuallyApproved")~~
|
||||||
|
("requestedOwnerGroupId" -> "updated-admin-group-id")))
|
||||||
|
|
||||||
|
|
||||||
|
val expected = RecordSet(
|
||||||
|
"1",
|
||||||
|
"TestRecordName",
|
||||||
|
RecordType.CNAME,
|
||||||
|
1000,
|
||||||
|
RecordSetStatus.Pending,
|
||||||
|
LocalDateTime.of(2010, Month.JANUARY, 1, 0, 0).toInstant(ZoneOffset.UTC),
|
||||||
|
records = List(CNAMEData(Fqdn("cname.data."))),
|
||||||
|
ownerGroupId = Some("updated-admin-group-id"),
|
||||||
|
recordSetGroupChange = Some(OwnerShipTransfer(ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyApproved,requestedOwnerGroupId = Some("updated-admin-group-id")))
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
val actual = recordSetJValue.extract[RecordSet]
|
||||||
|
anonymize(actual) shouldBe anonymize(expected)
|
||||||
|
anonymize(actual).recordSetGroupChange.get.ownerShipTransferStatus shouldBe OwnerShipTransferStatus.ManuallyApproved
|
||||||
|
anonymize(actual).recordSetGroupChange.get.requestedOwnerGroupId shouldBe Some("updated-admin-group-id")
|
||||||
|
anonymize(actual).ownerGroupId shouldBe Some("updated-admin-group-id")
|
||||||
|
}
|
||||||
|
|
||||||
|
"request a owner ship transfer" in {
|
||||||
|
val recordSetJValue: JValue =
|
||||||
|
("zoneId" -> "1") ~~
|
||||||
|
("name" -> "TestRecordName") ~~
|
||||||
|
("type" -> "CNAME") ~~
|
||||||
|
("ttl" -> 1000) ~~
|
||||||
|
("status" -> "Pending") ~~
|
||||||
|
("records" -> List("cname" -> "cname.data ")) ~~
|
||||||
|
("ownerGroupId" -> "updated-ok-group-id") ~~
|
||||||
|
("recordSetGroupChange" -> Some(("ownerShipTransferStatus" -> "Requested")~~
|
||||||
|
("requestedOwnerGroupId" -> "updated-admin-group-id")))
|
||||||
|
|
||||||
|
|
||||||
|
val expected = RecordSet(
|
||||||
|
"1",
|
||||||
|
"TestRecordName",
|
||||||
|
RecordType.CNAME,
|
||||||
|
1000,
|
||||||
|
RecordSetStatus.Pending,
|
||||||
|
LocalDateTime.of(2010, Month.JANUARY, 1, 0, 0).toInstant(ZoneOffset.UTC),
|
||||||
|
records = List(CNAMEData(Fqdn("cname.data."))),
|
||||||
|
ownerGroupId = Some("updated-ok-group-id"),
|
||||||
|
recordSetGroupChange = Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.Requested,
|
||||||
|
requestedOwnerGroupId = Some("updated-admin-group-id")))
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
val actual = recordSetJValue.extract[RecordSet]
|
||||||
|
anonymize(actual) shouldBe anonymize(expected)
|
||||||
|
anonymize(actual).recordSetGroupChange.get.ownerShipTransferStatus shouldBe OwnerShipTransferStatus.Requested
|
||||||
|
anonymize(actual).recordSetGroupChange.get.requestedOwnerGroupId shouldBe Some("updated-admin-group-id")
|
||||||
|
anonymize(actual).ownerGroupId shouldBe Some("updated-ok-group-id")
|
||||||
|
}
|
||||||
|
|
||||||
|
"request a owner ship transfer which will change to pending review" in {
|
||||||
|
val recordSetJValue: JValue =
|
||||||
|
("zoneId" -> "1") ~~
|
||||||
|
("name" -> "TestRecordName") ~~
|
||||||
|
("type" -> "CNAME") ~~
|
||||||
|
("ttl" -> 1000) ~~
|
||||||
|
("status" -> "Pending") ~~
|
||||||
|
("records" -> List("cname" -> "cname.data ")) ~~
|
||||||
|
("ownerGroupId" -> "updated-ok-group-id") ~~
|
||||||
|
("recordSetGroupChange" -> Some(("ownerShipTransferStatus" -> "PendingReview")~~
|
||||||
|
("requestedOwnerGroupId" -> "updated-admin-group-id")))
|
||||||
|
|
||||||
|
|
||||||
|
val expected = RecordSet(
|
||||||
|
"1",
|
||||||
|
"TestRecordName",
|
||||||
|
RecordType.CNAME,
|
||||||
|
1000,
|
||||||
|
RecordSetStatus.Pending,
|
||||||
|
LocalDateTime.of(2010, Month.JANUARY, 1, 0, 0).toInstant(ZoneOffset.UTC),
|
||||||
|
records = List(CNAMEData(Fqdn("cname.data."))),
|
||||||
|
ownerGroupId = Some("updated-ok-group-id"),
|
||||||
|
recordSetGroupChange = Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.PendingReview,
|
||||||
|
requestedOwnerGroupId = Some("updated-admin-group-id")))
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
val actual = recordSetJValue.extract[RecordSet]
|
||||||
|
anonymize(actual) shouldBe anonymize(expected)
|
||||||
|
anonymize(actual).recordSetGroupChange.get.ownerShipTransferStatus shouldBe OwnerShipTransferStatus.PendingReview
|
||||||
|
anonymize(actual).recordSetGroupChange.get.requestedOwnerGroupId shouldBe Some("updated-admin-group-id")
|
||||||
|
anonymize(actual).ownerGroupId shouldBe Some("updated-ok-group-id")
|
||||||
|
}
|
||||||
|
|
||||||
|
"cancel a owner ship transfer request" in {
|
||||||
|
val recordSetJValue: JValue =
|
||||||
|
("zoneId" -> "1") ~~
|
||||||
|
("name" -> "TestRecordName") ~~
|
||||||
|
("type" -> "CNAME") ~~
|
||||||
|
("ttl" -> 1000) ~~
|
||||||
|
("status" -> "Pending") ~~
|
||||||
|
("records" -> List("cname" -> "cname.data ")) ~~
|
||||||
|
("ownerGroupId" -> "updated-ok-group-id") ~~
|
||||||
|
("recordSetGroupChange" -> Some(("ownerShipTransferStatus" -> "Cancelled")~~
|
||||||
|
("requestedOwnerGroupId" -> "updated-admin-group-id")))
|
||||||
|
|
||||||
|
|
||||||
|
val expected = RecordSet(
|
||||||
|
"1",
|
||||||
|
"TestRecordName",
|
||||||
|
RecordType.CNAME,
|
||||||
|
1000,
|
||||||
|
RecordSetStatus.Pending,
|
||||||
|
LocalDateTime.of(2010, Month.JANUARY, 1, 0, 0).toInstant(ZoneOffset.UTC),
|
||||||
|
records = List(CNAMEData(Fqdn("cname.data."))),
|
||||||
|
ownerGroupId = Some("updated-ok-group-id"),
|
||||||
|
recordSetGroupChange = Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.Cancelled,
|
||||||
|
requestedOwnerGroupId = Some("updated-admin-group-id")))
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
val actual = recordSetJValue.extract[RecordSet]
|
||||||
|
anonymize(actual) shouldBe anonymize(expected)
|
||||||
|
anonymize(actual).recordSetGroupChange.get.ownerShipTransferStatus shouldBe OwnerShipTransferStatus.Cancelled
|
||||||
|
anonymize(actual).recordSetGroupChange.get.requestedOwnerGroupId shouldBe Some("updated-admin-group-id")
|
||||||
|
anonymize(actual).ownerGroupId shouldBe Some("updated-ok-group-id")
|
||||||
|
}
|
||||||
|
|
||||||
|
"manually-reject a owner ship transfer request" in {
|
||||||
|
val recordSetJValue: JValue =
|
||||||
|
("zoneId" -> "1") ~~
|
||||||
|
("name" -> "TestRecordName") ~~
|
||||||
|
("type" -> "CNAME") ~~
|
||||||
|
("ttl" -> 1000) ~~
|
||||||
|
("status" -> "Pending") ~~
|
||||||
|
("records" -> List("cname" -> "cname.data ")) ~~
|
||||||
|
("ownerGroupId" -> "updated-ok-group-id") ~~
|
||||||
|
("recordSetGroupChange" -> Some(("ownerShipTransferStatus" -> "ManuallyRejected")~~
|
||||||
|
("requestedOwnerGroupId" -> "updated-admin-group-id")))
|
||||||
|
|
||||||
|
|
||||||
|
val expected = RecordSet(
|
||||||
|
"1",
|
||||||
|
"TestRecordName",
|
||||||
|
RecordType.CNAME,
|
||||||
|
1000,
|
||||||
|
RecordSetStatus.Pending,
|
||||||
|
LocalDateTime.of(2010, Month.JANUARY, 1, 0, 0).toInstant(ZoneOffset.UTC),
|
||||||
|
records = List(CNAMEData(Fqdn("cname.data."))),
|
||||||
|
ownerGroupId = Some("updated-ok-group-id"),
|
||||||
|
recordSetGroupChange = Some(OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.ManuallyRejected,
|
||||||
|
requestedOwnerGroupId = Some("updated-admin-group-id")))
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
val actual = recordSetJValue.extract[RecordSet]
|
||||||
|
anonymize(actual) shouldBe anonymize(expected)
|
||||||
|
anonymize(actual).recordSetGroupChange.get.ownerShipTransferStatus shouldBe OwnerShipTransferStatus.ManuallyRejected
|
||||||
|
anonymize(actual).recordSetGroupChange.get.requestedOwnerGroupId shouldBe Some("updated-admin-group-id")
|
||||||
|
anonymize(actual).ownerGroupId shouldBe Some("updated-ok-group-id")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,13 @@ message RecordSet {
|
|||||||
repeated RecordData record = 9;
|
repeated RecordData record = 9;
|
||||||
required string account = 10;
|
required string account = 10;
|
||||||
optional string ownerGroupId = 11;
|
optional string ownerGroupId = 11;
|
||||||
optional string fqdn = 12;
|
optional ownerShipTransfer recordSetGroupChange = 12;
|
||||||
|
optional string fqdn = 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ownerShipTransfer {
|
||||||
|
optional string requestedOwnerGroupId = 1;
|
||||||
|
optional string ownerShipTransferStatus = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RecordSetChange {
|
message RecordSetChange {
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package vinyldns.core.domain.record
|
package vinyldns.core.domain.record
|
||||||
|
|
||||||
|
import vinyldns.core.domain.record.OwnerShipTransferStatus.OwnerShipTransferStatus
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
@ -33,6 +35,11 @@ object RecordSetStatus extends Enumeration {
|
|||||||
val Active, Inactive, Pending, PendingUpdate, PendingDelete = Value
|
val Active, Inactive, Pending, PendingUpdate, PendingDelete = Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object OwnerShipTransferStatus extends Enumeration {
|
||||||
|
type OwnerShipTransferStatus = Value
|
||||||
|
val AutoApproved, Cancelled, ManuallyApproved, ManuallyRejected, Requested, PendingReview, None = Value
|
||||||
|
}
|
||||||
|
|
||||||
import RecordSetStatus._
|
import RecordSetStatus._
|
||||||
import RecordType._
|
import RecordType._
|
||||||
|
|
||||||
@ -48,6 +55,7 @@ case class RecordSet(
|
|||||||
id: String = UUID.randomUUID().toString,
|
id: String = UUID.randomUUID().toString,
|
||||||
account: String = "system",
|
account: String = "system",
|
||||||
ownerGroupId: Option[String] = None,
|
ownerGroupId: Option[String] = None,
|
||||||
|
recordSetGroupChange: Option[OwnerShipTransfer] = None,
|
||||||
fqdn: Option[String] = None
|
fqdn: Option[String] = None
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -69,10 +77,25 @@ case class RecordSet(
|
|||||||
sb.append("account=\"").append(account).append("\"; ")
|
sb.append("account=\"").append(account).append("\"; ")
|
||||||
sb.append("status=\"").append(status.toString).append("\"; ")
|
sb.append("status=\"").append(status.toString).append("\"; ")
|
||||||
sb.append("records=\"").append(records.toString).append("\"; ")
|
sb.append("records=\"").append(records.toString).append("\"; ")
|
||||||
sb.append("ownerGroupId=\"").append(ownerGroupId).append("\"")
|
sb.append("ownerGroupId=\"").append(ownerGroupId).append("\"; ")
|
||||||
|
sb.append("recordSetGroupChange=\"").append(recordSetGroupChange.toString).append("\"; ")
|
||||||
sb.append("fqdn=\"").append(fqdn).append("\"")
|
sb.append("fqdn=\"").append(fqdn).append("\"")
|
||||||
sb.append("]")
|
sb.append("]")
|
||||||
|
|
||||||
sb.toString
|
sb.toString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case class OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus: OwnerShipTransferStatus,
|
||||||
|
requestedOwnerGroupId: Option[String] = None
|
||||||
|
){
|
||||||
|
|
||||||
|
override def toString: String = {
|
||||||
|
val sb = new StringBuilder
|
||||||
|
sb.append("OwnerShipTransfer: [")
|
||||||
|
sb.append("ownerShipTransferStatus=\"").append(ownerShipTransferStatus.toString).append("\"; ")
|
||||||
|
sb.append("requestedOwnerGroupId=\"").append(requestedOwnerGroupId.toString).append("\"")
|
||||||
|
sb.append("]")
|
||||||
|
sb.toString
|
||||||
|
}}
|
||||||
|
|
||||||
|
@ -15,21 +15,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
package vinyldns.core.notifier
|
package vinyldns.core.notifier
|
||||||
import vinyldns.core.domain.membership.UserRepository
|
import vinyldns.core.domain.membership.{GroupRepository, UserRepository}
|
||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import cats.effect.ContextShift
|
import cats.effect.ContextShift
|
||||||
|
|
||||||
object NotifierLoader {
|
object NotifierLoader {
|
||||||
|
|
||||||
def loadAll(configs: List[NotifierConfig], userRepository: UserRepository)(
|
def loadAll(configs: List[NotifierConfig], userRepository: UserRepository, groupRepository: GroupRepository)(
|
||||||
implicit cs: ContextShift[IO]
|
implicit cs: ContextShift[IO]
|
||||||
): IO[AllNotifiers] =
|
): IO[AllNotifiers] =
|
||||||
for {
|
for {
|
||||||
notifiers <- configs.parTraverse(load(_, userRepository))
|
notifiers <- configs.parTraverse(load(_, userRepository, groupRepository))
|
||||||
} yield AllNotifiers(notifiers)
|
} yield AllNotifiers(notifiers)
|
||||||
|
|
||||||
def load(config: NotifierConfig, userRepository: UserRepository): IO[Notifier] =
|
def load(config: NotifierConfig, userRepository: UserRepository, groupRepository: GroupRepository): IO[Notifier] =
|
||||||
for {
|
for {
|
||||||
provider <- IO(
|
provider <- IO(
|
||||||
Class
|
Class
|
||||||
@ -38,7 +38,7 @@ object NotifierLoader {
|
|||||||
.newInstance()
|
.newInstance()
|
||||||
.asInstanceOf[NotifierProvider]
|
.asInstanceOf[NotifierProvider]
|
||||||
)
|
)
|
||||||
notifier <- provider.load(config, userRepository)
|
notifier <- provider.load(config, userRepository, groupRepository)
|
||||||
} yield notifier
|
} yield notifier
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
package vinyldns.core.notifier
|
package vinyldns.core.notifier
|
||||||
import vinyldns.core.domain.membership.UserRepository
|
import vinyldns.core.domain.membership.{GroupRepository, UserRepository}
|
||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
|
|
||||||
trait NotifierProvider {
|
trait NotifierProvider {
|
||||||
def load(config: NotifierConfig, userRepository: UserRepository): IO[Notifier]
|
def load(config: NotifierConfig, userRepository: UserRepository, groupRepository: GroupRepository): IO[Notifier]
|
||||||
}
|
}
|
||||||
|
@ -100,9 +100,23 @@ trait ProtobufConversions {
|
|||||||
records =
|
records =
|
||||||
rs.getRecordList.asScala.map(rd => fromPB(rd, RecordType.withName(rs.getTyp))).toList,
|
rs.getRecordList.asScala.map(rd => fromPB(rd, RecordType.withName(rs.getTyp))).toList,
|
||||||
account = rs.getAccount,
|
account = rs.getAccount,
|
||||||
ownerGroupId = if (rs.hasOwnerGroupId) Some(rs.getOwnerGroupId) else None
|
ownerGroupId = if (rs.hasOwnerGroupId) Some(rs.getOwnerGroupId) else None ,
|
||||||
|
recordSetGroupChange = if (rs.hasRecordSetGroupChange) Some(fromPB(rs.getRecordSetGroupChange)) else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def fromPB(rsa: VinylDNSProto.ownerShipTransfer): OwnerShipTransfer =
|
||||||
|
record.OwnerShipTransfer(
|
||||||
|
ownerShipTransferStatus = OwnerShipTransferStatus.withName(rsa.getOwnerShipTransferStatus),
|
||||||
|
requestedOwnerGroupId = if (rsa.hasRequestedOwnerGroupId) Some(rsa.getRequestedOwnerGroupId) else None)
|
||||||
|
|
||||||
|
def toPB(rsa: OwnerShipTransfer): VinylDNSProto.ownerShipTransfer = {
|
||||||
|
val builder = VinylDNSProto.ownerShipTransfer
|
||||||
|
.newBuilder()
|
||||||
|
.setOwnerShipTransferStatus(rsa.ownerShipTransferStatus.toString)
|
||||||
|
rsa.requestedOwnerGroupId.foreach(id => builder.setRequestedOwnerGroupId(id))
|
||||||
|
builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
def fromPB(zn: VinylDNSProto.Zone): Zone = {
|
def fromPB(zn: VinylDNSProto.Zone): Zone = {
|
||||||
val pbStatus = zn.getStatus
|
val pbStatus = zn.getStatus
|
||||||
val status =
|
val status =
|
||||||
@ -377,6 +391,7 @@ trait ProtobufConversions {
|
|||||||
|
|
||||||
rs.updated.foreach(dt => builder.setUpdated(dt.toEpochMilli))
|
rs.updated.foreach(dt => builder.setUpdated(dt.toEpochMilli))
|
||||||
rs.ownerGroupId.foreach(id => builder.setOwnerGroupId(id))
|
rs.ownerGroupId.foreach(id => builder.setOwnerGroupId(id))
|
||||||
|
rs.recordSetGroupChange.foreach(rsg => builder.setRecordSetGroupChange(toPB(rsg)))
|
||||||
|
|
||||||
// Map the records, first map to bytes, and then map the bytes to a record data instance
|
// Map the records, first map to bytes, and then map the bytes to a record data instance
|
||||||
rs.records.map(toRecordData).foreach(rd => builder.addRecord(rd))
|
rs.records.map(toRecordData).foreach(rd => builder.addRecord(rd))
|
||||||
|
@ -37,7 +37,8 @@ object TestRecordSetData {
|
|||||||
RecordSetStatus.Active,
|
RecordSetStatus.Active,
|
||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
List(AData("10.1.1.1"))
|
List(AData("10.1.1.1")),
|
||||||
|
recordSetGroupChange = None
|
||||||
)
|
)
|
||||||
|
|
||||||
val abcRecord: RecordSet = RecordSet(
|
val abcRecord: RecordSet = RecordSet(
|
||||||
@ -48,7 +49,8 @@ object TestRecordSetData {
|
|||||||
RecordSetStatus.Pending,
|
RecordSetStatus.Pending,
|
||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
List(AAAAData("1:2:3:4:5:6:7:8"))
|
List(AAAAData("1:2:3:4:5:6:7:8")),
|
||||||
|
recordSetGroupChange= None
|
||||||
)
|
)
|
||||||
|
|
||||||
val aaaa: RecordSet = RecordSet(
|
val aaaa: RecordSet = RecordSet(
|
||||||
@ -59,7 +61,8 @@ object TestRecordSetData {
|
|||||||
RecordSetStatus.Pending,
|
RecordSetStatus.Pending,
|
||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
List(AAAAData("1:2:3:4:5:6:7:8"))
|
List(AAAAData("1:2:3:4:5:6:7:8")),
|
||||||
|
recordSetGroupChange= None
|
||||||
)
|
)
|
||||||
|
|
||||||
val aaaaOrigin: RecordSet = RecordSet(
|
val aaaaOrigin: RecordSet = RecordSet(
|
||||||
@ -70,7 +73,8 @@ object TestRecordSetData {
|
|||||||
RecordSetStatus.Pending,
|
RecordSetStatus.Pending,
|
||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
List(AAAAData("1:2:3:4:5:6:7:8"))
|
List(AAAAData("1:2:3:4:5:6:7:8")),
|
||||||
|
recordSetGroupChange= None
|
||||||
)
|
)
|
||||||
|
|
||||||
val cname: RecordSet = RecordSet(
|
val cname: RecordSet = RecordSet(
|
||||||
@ -81,7 +85,8 @@ object TestRecordSetData {
|
|||||||
RecordSetStatus.Pending,
|
RecordSetStatus.Pending,
|
||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
List(CNAMEData(Fqdn("cname")))
|
List(CNAMEData(Fqdn("cname"))),
|
||||||
|
recordSetGroupChange= None
|
||||||
)
|
)
|
||||||
|
|
||||||
val ptrIp4: RecordSet = RecordSet(
|
val ptrIp4: RecordSet = RecordSet(
|
||||||
@ -92,7 +97,8 @@ object TestRecordSetData {
|
|||||||
RecordSetStatus.Active,
|
RecordSetStatus.Active,
|
||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
List(PTRData(Fqdn("ptr")))
|
List(PTRData(Fqdn("ptr"))),
|
||||||
|
recordSetGroupChange= None
|
||||||
)
|
)
|
||||||
|
|
||||||
val ptrIp6: RecordSet = RecordSet(
|
val ptrIp6: RecordSet = RecordSet(
|
||||||
@ -103,7 +109,8 @@ object TestRecordSetData {
|
|||||||
RecordSetStatus.Active,
|
RecordSetStatus.Active,
|
||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
List(PTRData(Fqdn("ptr")))
|
List(PTRData(Fqdn("ptr"))),
|
||||||
|
recordSetGroupChange= None
|
||||||
)
|
)
|
||||||
|
|
||||||
val srv: RecordSet = RecordSet(
|
val srv: RecordSet = RecordSet(
|
||||||
@ -114,7 +121,8 @@ object TestRecordSetData {
|
|||||||
RecordSetStatus.Active,
|
RecordSetStatus.Active,
|
||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
List(SRVData(1, 2, 3, Fqdn("target")))
|
List(SRVData(1, 2, 3, Fqdn("target"))),
|
||||||
|
recordSetGroupChange= None
|
||||||
)
|
)
|
||||||
|
|
||||||
val naptr: RecordSet = RecordSet(
|
val naptr: RecordSet = RecordSet(
|
||||||
@ -125,7 +133,8 @@ object TestRecordSetData {
|
|||||||
RecordSetStatus.Active,
|
RecordSetStatus.Active,
|
||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
List(NAPTRData(1, 2, "S", "E2U+sip", "", Fqdn("target")))
|
List(NAPTRData(1, 2, "S", "E2U+sip", "", Fqdn("target"))),
|
||||||
|
recordSetGroupChange= None
|
||||||
)
|
)
|
||||||
|
|
||||||
val mx: RecordSet = RecordSet(
|
val mx: RecordSet = RecordSet(
|
||||||
@ -136,7 +145,8 @@ object TestRecordSetData {
|
|||||||
RecordSetStatus.Pending,
|
RecordSetStatus.Pending,
|
||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
List(MXData(3, Fqdn("mx")))
|
List(MXData(3, Fqdn("mx"))),
|
||||||
|
recordSetGroupChange= None
|
||||||
)
|
)
|
||||||
|
|
||||||
val ns: RecordSet = RecordSet(
|
val ns: RecordSet = RecordSet(
|
||||||
@ -147,7 +157,8 @@ object TestRecordSetData {
|
|||||||
RecordSetStatus.Active,
|
RecordSetStatus.Active,
|
||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
records = List(NSData(Fqdn("ns1.test.com")), NSData(Fqdn("ns2.test.com")))
|
records = List(NSData(Fqdn("ns1.test.com")), NSData(Fqdn("ns2.test.com"))),
|
||||||
|
recordSetGroupChange= None
|
||||||
)
|
)
|
||||||
|
|
||||||
val txt: RecordSet = RecordSet(
|
val txt: RecordSet = RecordSet(
|
||||||
@ -158,7 +169,8 @@ object TestRecordSetData {
|
|||||||
RecordSetStatus.Pending,
|
RecordSetStatus.Pending,
|
||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
List(TXTData("txt"))
|
List(TXTData("txt")),
|
||||||
|
recordSetGroupChange= None
|
||||||
)
|
)
|
||||||
|
|
||||||
// example at https://tools.ietf.org/html/rfc4034#page-18
|
// example at https://tools.ietf.org/html/rfc4034#page-18
|
||||||
@ -198,7 +210,12 @@ object TestRecordSetData {
|
|||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
None,
|
None,
|
||||||
List(AAAAData("1:2:3:4:5:6:7:8")),
|
List(AAAAData("1:2:3:4:5:6:7:8")),
|
||||||
ownerGroupId = Some(okGroup.id)
|
ownerGroupId = Some(okGroup.id),
|
||||||
|
recordSetGroupChange= None
|
||||||
|
)
|
||||||
|
|
||||||
|
val ownerShipTransfer: OwnerShipTransfer = OwnerShipTransfer(
|
||||||
|
OwnerShipTransferStatus.None
|
||||||
)
|
)
|
||||||
|
|
||||||
val sharedZoneRecordNoOwnerGroup: RecordSet =
|
val sharedZoneRecordNoOwnerGroup: RecordSet =
|
||||||
|
@ -19,7 +19,7 @@ package vinyldns.core.notifier
|
|||||||
import cats.scalatest.{EitherMatchers, EitherValues, ValidatedMatchers}
|
import cats.scalatest.{EitherMatchers, EitherValues, ValidatedMatchers}
|
||||||
import com.typesafe.config.{Config, ConfigFactory}
|
import com.typesafe.config.{Config, ConfigFactory}
|
||||||
import org.scalatestplus.mockito.MockitoSugar
|
import org.scalatestplus.mockito.MockitoSugar
|
||||||
import vinyldns.core.domain.membership.UserRepository
|
import vinyldns.core.domain.membership.{GroupRepository, UserRepository}
|
||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
import org.mockito.Mockito._
|
import org.mockito.Mockito._
|
||||||
|
|
||||||
@ -37,13 +37,13 @@ object MockNotifierProvider extends MockitoSugar {
|
|||||||
|
|
||||||
class MockNotifierProvider extends NotifierProvider {
|
class MockNotifierProvider extends NotifierProvider {
|
||||||
|
|
||||||
def load(config: NotifierConfig, userRepo: UserRepository): IO[Notifier] =
|
def load(config: NotifierConfig, userRepo: UserRepository, groupRepo: GroupRepository): IO[Notifier] =
|
||||||
IO.pure(MockNotifierProvider.mockNotifier)
|
IO.pure(MockNotifierProvider.mockNotifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
class FailingProvider extends NotifierProvider {
|
class FailingProvider extends NotifierProvider {
|
||||||
|
|
||||||
def load(config: NotifierConfig, userRepo: UserRepository): IO[Notifier] =
|
def load(config: NotifierConfig, userRepo: UserRepository, groupRepo: GroupRepository): IO[Notifier] =
|
||||||
IO.raiseError(new IllegalStateException("always failing"))
|
IO.raiseError(new IllegalStateException("always failing"))
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -63,6 +63,7 @@ class NotifierLoaderSpec
|
|||||||
val goodConfig = NotifierConfig("vinyldns.core.notifier.MockNotifierProvider", placeholderConfig)
|
val goodConfig = NotifierConfig("vinyldns.core.notifier.MockNotifierProvider", placeholderConfig)
|
||||||
|
|
||||||
val mockUserRepository: UserRepository = mock[UserRepository]
|
val mockUserRepository: UserRepository = mock[UserRepository]
|
||||||
|
val mockGroupRepository: GroupRepository = mock[GroupRepository]
|
||||||
|
|
||||||
import MockNotifierProvider._
|
import MockNotifierProvider._
|
||||||
|
|
||||||
@ -73,14 +74,13 @@ class NotifierLoaderSpec
|
|||||||
|
|
||||||
"return some notifier with no configs" in {
|
"return some notifier with no configs" in {
|
||||||
|
|
||||||
val notifier = NotifierLoader.loadAll(List.empty, mockUserRepository).unsafeRunSync()
|
val notifier = NotifierLoader.loadAll(List.empty, mockUserRepository, mockGroupRepository).unsafeRunSync()
|
||||||
|
|
||||||
notifier shouldBe a[AllNotifiers]
|
notifier shouldBe a[AllNotifiers]
|
||||||
notifier.notify(Notification(3)).unsafeRunSync() shouldBe (())
|
notifier.notify(Notification(3)).unsafeRunSync() shouldBe (())
|
||||||
}
|
}
|
||||||
|
|
||||||
"return a notifier for valid config of one notifier" in {
|
"return a notifier for valid config of one notifier" in {
|
||||||
val notifier = NotifierLoader.loadAll(List(goodConfig), mockUserRepository).unsafeRunSync()
|
val notifier = NotifierLoader.loadAll(List(goodConfig), mockUserRepository, mockGroupRepository).unsafeRunSync()
|
||||||
|
|
||||||
notifier shouldNot be(null)
|
notifier shouldNot be(null)
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ class NotifierLoaderSpec
|
|||||||
|
|
||||||
"return a notifier for valid config of multiple notifiers" in {
|
"return a notifier for valid config of multiple notifiers" in {
|
||||||
val notifier =
|
val notifier =
|
||||||
NotifierLoader.loadAll(List(goodConfig, goodConfig), mockUserRepository).unsafeRunSync()
|
NotifierLoader.loadAll(List(goodConfig, goodConfig), mockUserRepository, mockGroupRepository).unsafeRunSync()
|
||||||
|
|
||||||
notifier shouldNot be(null)
|
notifier shouldNot be(null)
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ class NotifierLoaderSpec
|
|||||||
val badProvider =
|
val badProvider =
|
||||||
NotifierConfig("vinyldns.core.notifier.NotFoundNotifierProvider", placeholderConfig)
|
NotifierConfig("vinyldns.core.notifier.NotFoundNotifierProvider", placeholderConfig)
|
||||||
|
|
||||||
val load = NotifierLoader.loadAll(List(goodConfig, badProvider), mockUserRepository)
|
val load = NotifierLoader.loadAll(List(goodConfig, badProvider), mockUserRepository, mockGroupRepository)
|
||||||
|
|
||||||
a[ClassNotFoundException] shouldBe thrownBy(load.unsafeRunSync())
|
a[ClassNotFoundException] shouldBe thrownBy(load.unsafeRunSync())
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ class NotifierLoaderSpec
|
|||||||
val exceptionProvider =
|
val exceptionProvider =
|
||||||
NotifierConfig("vinyldns.core.notifier.FailingProvider", placeholderConfig)
|
NotifierConfig("vinyldns.core.notifier.FailingProvider", placeholderConfig)
|
||||||
|
|
||||||
val load = NotifierLoader.loadAll(List(goodConfig, exceptionProvider), mockUserRepository)
|
val load = NotifierLoader.loadAll(List(goodConfig, exceptionProvider), mockUserRepository, mockGroupRepository)
|
||||||
|
|
||||||
a[IllegalStateException] shouldBe thrownBy(load.unsafeRunSync())
|
a[IllegalStateException] shouldBe thrownBy(load.unsafeRunSync())
|
||||||
}
|
}
|
||||||
|
@ -133,6 +133,7 @@
|
|||||||
<th class="col-md-4">Record Data</th>
|
<th class="col-md-4">Record Data</th>
|
||||||
@if(meta.sharedDisplayEnabled) {
|
@if(meta.sharedDisplayEnabled) {
|
||||||
<th ng-if="zoneInfo.shared">Owner Group Name</th>
|
<th ng-if="zoneInfo.shared">Owner Group Name</th>
|
||||||
|
<th ng-if="zoneInfo.shared">OwnerShip Transfer Status</th>
|
||||||
}
|
}
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -148,8 +149,8 @@
|
|||||||
{{record.name}}
|
{{record.name}}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="wrap-long-text">{{record.type}}</td>
|
<td>{{record.type}}</td>
|
||||||
<td class="wrap-long-text">{{record.ttl}}</td>
|
<td>{{record.ttl}}</td>
|
||||||
<td class="record-data">
|
<td class="record-data">
|
||||||
<span ng-if="record.accessLevel != 'NoAccess'">
|
<span ng-if="record.accessLevel != 'NoAccess'">
|
||||||
<span ng-if="record.type == 'A'">
|
<span ng-if="record.type == 'A'">
|
||||||
@ -336,19 +337,42 @@
|
|||||||
@if(meta.sharedDisplayEnabled) {
|
@if(meta.sharedDisplayEnabled) {
|
||||||
<td ng-if="zoneInfo.shared">
|
<td ng-if="zoneInfo.shared">
|
||||||
<a ng-if="record.ownerGroupName && canAccessGroup(record.ownerGroupId)" ng-bind="record.ownerGroupName" href="/groups/{{record.ownerGroupId}}"></a>
|
<a ng-if="record.ownerGroupName && canAccessGroup(record.ownerGroupId)" ng-bind="record.ownerGroupName" href="/groups/{{record.ownerGroupId}}"></a>
|
||||||
<span ng-if="record.ownerGroupName && !canAccessGroup(record.ownerGroupId)" ng-bind="record.ownerGroupName"></span>
|
<span ng-if="record.ownerGroupName && !canAccessGroup(record.ownerGroupId)" ng-bind="record.ownerGroupName" ></span> <span ng-if="!record.ownerGroupId">Unowned</span>
|
||||||
<span ng-if="!record.ownerGroupId">Unowned</span>
|
|
||||||
<span ng-if="record.ownerGroupId && !record.ownerGroupName" class="text-danger" data-toggle="tooltip" data-placement="top"
|
<span ng-if="record.ownerGroupId && !record.ownerGroupName" class="text-danger" data-toggle="tooltip" data-placement="top"
|
||||||
title="Group with ID {{record.ownerGroupId}} no longer exists."><span class="fa fa-warning"></span> Group deleted</span>
|
title="Group with ID {{record.ownerGroupId}} no longer exists."><span class="fa fa-warning"></span> Group deleted</span>
|
||||||
</td>
|
</td>
|
||||||
|
<td ng-if="zoneInfo.shared">
|
||||||
|
<span class="label label-success"
|
||||||
|
ng-if= "record.recordSetGroupChange.ownerShipTransferStatus == 'ManuallyApproved' ||
|
||||||
|
record.recordSetGroupChange.ownerShipTransferStatus == 'AutoApproved'">Approved</span>
|
||||||
|
<span class="label label-danger"
|
||||||
|
ng-if= "record.recordSetGroupChange.ownerShipTransferStatus == 'ManuallyRejected'">Rejected</span>
|
||||||
|
<span ng-if= "record.recordSetGroupChange.ownerShipTransferStatus == 'None'"></span>
|
||||||
|
<span class="label label-default"
|
||||||
|
ng-if= "record.recordSetGroupChange.ownerShipTransferStatus == 'Cancelled'">Cancelled</span>
|
||||||
|
<span class="label label-info"
|
||||||
|
ng-if= "record.recordSetGroupChange.ownerShipTransferStatus == 'PendingReview'">Pending Action</span>
|
||||||
|
</td>
|
||||||
}
|
}
|
||||||
<td>
|
<td>
|
||||||
<span ng-if="(record.canBeEdited && record.accessLevel != 'NoAccess' && record.accessLevel != 'Read')">
|
<span ng-if="(record.canBeEdited && record.accessLevel != 'NoAccess' && record.accessLevel != 'Read')">
|
||||||
<div class="table-form-group">
|
<div class="table-form-group">
|
||||||
<span><button class="btn btn-info btn-sm" ng-click="editRecord(record)">Update</button></span>
|
<span><button class="btn btn-info btn-sm" ng-click="editRecord(record)">Update</button></span>
|
||||||
<span ng-if="record.accessLevel == 'Delete'"><button id="delete-record-{{record.name}}-button" class="btn btn-danger btn-sm btn-rounded" ng-click="deleteRecord(record)">Delete</button></span>
|
<span ng-if="record.accessLevel == 'Delete'"><button id="delete-record-{{record.name}}-button" class="btn btn-danger btn-sm btn-rounded" ng-click="deleteRecord(record)">Delete</button></span>
|
||||||
|
<span ng-if="zoneInfo.shared && record.ownerGroupId == undefined && record.canBeEdited ">
|
||||||
|
<button class="btn btn-info btn-sm" ng-click="requestOwnerShip(record)">Request</button>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
<span ng-if="zoneInfo.shared && record.ownerGroupId != undefined && record.canBeEdited"
|
||||||
|
ng-switch="record.isCurrentRecordSetOwner">
|
||||||
|
<span ng-switch-when="false">
|
||||||
|
<button class="btn btn-info btn-sm" ng-click="requestOwnerShipTransfer(record, true)">Request</button>
|
||||||
|
</span>
|
||||||
|
<span ng-switch-when="true" ng-if= record.recordSetGroupChange.ownerShipTransferStatus=="PendingReview">
|
||||||
|
<button class="btn btn-info btn-sm" ng-click="requestOwnerShipTransfer(record, false)">Close Request</button>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -31,6 +31,7 @@ angular.module('controller.records', [])
|
|||||||
$scope.alerts = [];
|
$scope.alerts = [];
|
||||||
|
|
||||||
$scope.recordTypes = ['A', 'AAAA', 'CNAME', 'DS', 'MX', 'NS', 'PTR', 'SRV', 'NAPTR', 'SSHFP', 'TXT'];
|
$scope.recordTypes = ['A', 'AAAA', 'CNAME', 'DS', 'MX', 'NS', 'PTR', 'SRV', 'NAPTR', 'SSHFP', 'TXT'];
|
||||||
|
$scope.ownerShipTransferStatus = ['AutoApproved', 'Cancelled', 'ManuallyApproved', 'ManuallyRejected', 'Requested', 'PendingReview'];
|
||||||
$scope.readRecordTypes = ['A', 'AAAA', 'CNAME', 'DS', 'MX', 'NS', 'PTR', "SOA", 'SRV', 'NAPTR', 'SSHFP', 'TXT'];
|
$scope.readRecordTypes = ['A', 'AAAA', 'CNAME', 'DS', 'MX', 'NS', 'PTR', "SOA", 'SRV', 'NAPTR', 'SSHFP', 'TXT'];
|
||||||
$scope.selectedRecordTypes = [];
|
$scope.selectedRecordTypes = [];
|
||||||
$scope.naptrFlags = ["U", "S", "A", "P"];
|
$scope.naptrFlags = ["U", "S", "A", "P"];
|
||||||
@ -46,6 +47,7 @@ angular.module('controller.records', [])
|
|||||||
{name: '(254) PRIVATEOID', number: 254}]
|
{name: '(254) PRIVATEOID', number: 254}]
|
||||||
$scope.dsDigestTypes = [{name: '(1) SHA1', number: 1}, {name: '(2) SHA256', number: 2}, {name: '(3) GOSTR341194', number: 3}, {name: '(4) SHA384', number: 4}]
|
$scope.dsDigestTypes = [{name: '(1) SHA1', number: 1}, {name: '(2) SHA256', number: 2}, {name: '(3) GOSTR341194', number: 3}, {name: '(4) SHA384', number: 4}]
|
||||||
$scope.records = {};
|
$scope.records = {};
|
||||||
|
$scope.isOwnerShipRequest = true;
|
||||||
$scope.recordsetChangesPreview = {};
|
$scope.recordsetChangesPreview = {};
|
||||||
$scope.recordsetChanges = {};
|
$scope.recordsetChanges = {};
|
||||||
$scope.currentRecord = {};
|
$scope.currentRecord = {};
|
||||||
@ -57,6 +59,12 @@ angular.module('controller.records', [])
|
|||||||
var loadZonesPromise;
|
var loadZonesPromise;
|
||||||
var loadRecordsPromise;
|
var loadRecordsPromise;
|
||||||
|
|
||||||
|
$scope.ownerShipTransferApproverStatus = [{value: 'ManuallyApproved' , label: 'Approve'},
|
||||||
|
{value: 'ManuallyRejected', label: 'Reject'}];
|
||||||
|
|
||||||
|
$scope.ownerShipTransferRequestorStatus = [{value: 'Requested', label: 'Request'},
|
||||||
|
{value: 'Cancelled', label: 'Cancel'}];
|
||||||
|
|
||||||
$scope.recordModalState = {
|
$scope.recordModalState = {
|
||||||
CREATE: 0,
|
CREATE: 0,
|
||||||
UPDATE: 1,
|
UPDATE: 1,
|
||||||
@ -83,6 +91,7 @@ angular.module('controller.records', [])
|
|||||||
$scope.isZoneAdmin = false;
|
$scope.isZoneAdmin = false;
|
||||||
$scope.canReadZone = false;
|
$scope.canReadZone = false;
|
||||||
$scope.canCreateRecords = false;
|
$scope.canCreateRecords = false;
|
||||||
|
$scope.isCurrentRecordSetOwner = false;
|
||||||
|
|
||||||
// paging status for recordsets
|
// paging status for recordsets
|
||||||
var recordsPaging = pagingService.getNewPagingParams(100);
|
var recordsPaging = pagingService.getNewPagingParams(100);
|
||||||
@ -94,6 +103,43 @@ angular.module('controller.records', [])
|
|||||||
* Modal control functions
|
* Modal control functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
$scope.recordSetGroupOwnerShipStatus = function recordSetGroupOwnerShipStatus(groupId, profileId, record) {
|
||||||
|
function success(response) {
|
||||||
|
var ownerShipTransferStatus;
|
||||||
|
if (response.data.members.some(x => x.id === profileId)){
|
||||||
|
ownerShipTransferStatus = $scope.ownerShipTransferApproverStatus;
|
||||||
|
$scope.currentOwnerShipTransferApprover= true;
|
||||||
|
record.isCurrentRecordSetOwner = true;}
|
||||||
|
else{ownerShipTransferStatus = $scope.ownerShipTransferRequestorStatus;
|
||||||
|
$scope.currentOwnerShipTransferApprover= false;
|
||||||
|
record.isCurrentRecordSetOwner= false;}
|
||||||
|
$scope.ownerShipTransferStatus = ownerShipTransferStatus
|
||||||
|
}
|
||||||
|
return groupsService
|
||||||
|
.getGroupMemberList(groupId)
|
||||||
|
.then(success)
|
||||||
|
.catch(function (error) {
|
||||||
|
handleError(error, 'groupsService::getGroupMemberList-failure');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function getGroup(groupId) {
|
||||||
|
if (groupId != undefined && groupId != "null"){
|
||||||
|
$log.log('groupsService::getGroup-success');
|
||||||
|
function success(response) {
|
||||||
|
$scope.recordSetRequestedOwnerShipName = response.data.name;
|
||||||
|
|
||||||
|
}
|
||||||
|
return groupsService
|
||||||
|
.getGroup(groupId)
|
||||||
|
.then(success)
|
||||||
|
.catch(function (error) {
|
||||||
|
handleError(error, 'groupsService::getGroup-failure');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {$scope.recordSetRequestedOwnerShipName = "None";}
|
||||||
|
};
|
||||||
|
|
||||||
$scope.deleteRecord = function(record) {
|
$scope.deleteRecord = function(record) {
|
||||||
$scope.currentRecord = angular.copy(record);
|
$scope.currentRecord = angular.copy(record);
|
||||||
$scope.recordModal = {
|
$scope.recordModal = {
|
||||||
@ -132,6 +178,8 @@ angular.module('controller.records', [])
|
|||||||
|
|
||||||
$scope.editRecord = function(record) {
|
$scope.editRecord = function(record) {
|
||||||
$scope.currentRecord = angular.copy(record);
|
$scope.currentRecord = angular.copy(record);
|
||||||
|
$scope.currentRecord.recordSetGroupChange = angular.copy(record.recordSetGroupChange);
|
||||||
|
getGroup($scope.currentRecord.recordSetGroupChange.requestedOwnerGroupId);
|
||||||
$scope.recordModal = {
|
$scope.recordModal = {
|
||||||
previous: angular.copy(record),
|
previous: angular.copy(record),
|
||||||
action: $scope.recordModalState.UPDATE,
|
action: $scope.recordModalState.UPDATE,
|
||||||
@ -145,6 +193,81 @@ angular.module('controller.records', [])
|
|||||||
$("#record_modal").modal("show");
|
$("#record_modal").modal("show");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.requestOwnerShip = function(record) {
|
||||||
|
$scope.currentRecord = angular.copy(record);
|
||||||
|
$scope.currentRecord.recordSetGroupChange.ownerShipTransferStatus = angular.copy("AutoApproved")
|
||||||
|
$scope.recordModal = {
|
||||||
|
action: $scope.recordModalState.UPDATE,
|
||||||
|
title: "Request OwnerShip transfer",
|
||||||
|
basics: $scope.recordModalParams.readOnly,
|
||||||
|
details: $scope.recordModalParams.editable,
|
||||||
|
sharedZone: $scope.zoneInfo.shared,
|
||||||
|
sharedDisplayEnabled: $scope.sharedDisplayEnabled,
|
||||||
|
isCurrentRecordOwnerGroup : false
|
||||||
|
};
|
||||||
|
$scope.recordOwnerShipForm.$setPristine();
|
||||||
|
$("#record_modal_ownership").modal("show");
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.requestOwnerShipTransfer = function(record, isOwnerShipRequest) {
|
||||||
|
$scope.currentRecord = angular.copy(record);
|
||||||
|
$scope.currentRecord.recordSetGroupChange = angular.copy(record.recordSetGroupChange);
|
||||||
|
$scope.recordModal = {
|
||||||
|
previous: angular.copy(record),
|
||||||
|
action: $scope.recordModalState.UPDATE,
|
||||||
|
title: "Request OwnerShip transfer",
|
||||||
|
basics: $scope.recordModalParams.readOnly,
|
||||||
|
details: $scope.recordModalParams.editable,
|
||||||
|
sharedZone: $scope.zoneInfo.shared,
|
||||||
|
sharedDisplayEnabled: $scope.sharedDisplayEnabled,
|
||||||
|
isCurrentRecordOwnerGroup : false
|
||||||
|
};
|
||||||
|
|
||||||
|
var currentRecordOwnerGroupId = $scope.currentRecord.ownerGroupId;
|
||||||
|
if (isOwnerShipRequest && $scope.currentRecord.recordSetGroupChange.ownerShipTransferStatus != "PendingReview") {
|
||||||
|
$scope.currentRecord.recordSetGroupChange.requestedOwnerGroupId = angular.copy(null);
|
||||||
|
$scope.currentRecord.recordSetGroupChange.ownerShipTransferStatus = angular.copy(null);
|
||||||
|
}else ($scope.currentRecord.recordSetGroupChange.ownerShipTransferStatus = angular.copy(null))
|
||||||
|
getGroup($scope.currentRecord.recordSetGroupChange.requestedOwnerGroupId);
|
||||||
|
$scope.ownerShipTransferApprover = false;
|
||||||
|
$scope.ownerShipTransferRequestor = false;
|
||||||
|
|
||||||
|
if (currentRecordOwnerGroupId != undefined){$scope.recordModal.isCurrentRecordOwnerGroup = true
|
||||||
|
}else{ $scope.recordModal.isCurrentRecordOwnerGroup = false }
|
||||||
|
|
||||||
|
if ($scope.zoneInfo.shared == true && $scope.recordModal.isCurrentRecordOwnerGroup){
|
||||||
|
$scope.recordSetGroupOwnerShipStatus(currentRecordOwnerGroupId, $scope.profile.id, record);
|
||||||
|
$scope.ownerShipTransferApproverStatus.forEach(function(ownerShipTransferApproverStatus, index) {
|
||||||
|
if (ownerShipTransferApproverStatus.value.indexOf($scope.currentRecord.recordSetGroupChange.ownerShipTransferStatus) > -1)
|
||||||
|
{$scope.ownerShipTransferApprover = true}else{$scope.ownerShipTransferRequestor = true}})
|
||||||
|
}
|
||||||
|
$scope.recordOwnerShipForm.$setPristine();
|
||||||
|
$("#record_modal_ownership_transfer").modal("show");
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.requestedOwnerShip = function() {
|
||||||
|
|
||||||
|
var record = angular.copy($scope.currentRecord);
|
||||||
|
record['onlyFour'] = true;
|
||||||
|
if ($scope.recordOwnerShipForm.$valid) {
|
||||||
|
updateRecordSet(record);
|
||||||
|
$scope.recordOwnerShipForm.$setPristine();
|
||||||
|
$("#record_modal_ownership").modal('hide');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.submitRequestedOwnerShipTransfer = function () {
|
||||||
|
var record = angular.copy($scope.currentRecord);
|
||||||
|
record['onlyFour'] = true;
|
||||||
|
var invalidRecordOwnerShipForm = $scope.recordOwnerShipForm.ownerGroupStatus.$viewValue != null &&
|
||||||
|
$scope.recordOwnerShipForm.ownerGroupStatus.$viewValue
|
||||||
|
if ($scope.recordOwnerShipForm.$valid && invalidRecordOwnerShipForm) {
|
||||||
|
updateRecordSet(record);
|
||||||
|
$scope.recordOwnerShipForm.$setPristine();
|
||||||
|
$("#record_modal_ownership_transfer").modal('hide');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
$scope.confirmUpdate = function() {
|
$scope.confirmUpdate = function() {
|
||||||
$scope.recordModal.action = $scope.recordModalState.CONFIRM_UPDATE;
|
$scope.recordModal.action = $scope.recordModalState.CONFIRM_UPDATE;
|
||||||
$scope.recordModal.details = $scope.recordModalParams.readOnly;
|
$scope.recordModal.details = $scope.recordModalParams.readOnly;
|
||||||
@ -190,11 +313,15 @@ angular.module('controller.records', [])
|
|||||||
|
|
||||||
$scope.submitUpdateRecord = function () {
|
$scope.submitUpdateRecord = function () {
|
||||||
var record = angular.copy($scope.currentRecord);
|
var record = angular.copy($scope.currentRecord);
|
||||||
|
if(record.recordSetGroupChange.requestedOwnerGroupId != undefined){
|
||||||
|
if (record.ownerGroupId != $scope.recordModal.previous.ownerGroupId && $scope.isZoneAdmin){
|
||||||
|
record.recordSetGroupChange.requestedOwnerGroupId = angular.copy(record.ownerGroupId);
|
||||||
|
record.recordSetGroupChange.ownerShipTransferStatus = angular.copy("ManuallyApproved");
|
||||||
|
}
|
||||||
|
}
|
||||||
record['onlyFour'] = true;
|
record['onlyFour'] = true;
|
||||||
|
|
||||||
if ($scope.addRecordForm.$valid) {
|
if ($scope.addRecordForm.$valid) {
|
||||||
updateRecordSet(record);
|
updateRecordSet(record);
|
||||||
|
|
||||||
$scope.addRecordForm.$setPristine();
|
$scope.addRecordForm.$setPristine();
|
||||||
$("#record_modal").modal('hide');
|
$("#record_modal").modal('hide');
|
||||||
}
|
}
|
||||||
@ -496,6 +623,11 @@ angular.module('controller.records', [])
|
|||||||
angular.forEach(records, function(record) {
|
angular.forEach(records, function(record) {
|
||||||
newRecords.push(recordsService.toDisplayRecord(record, $scope.zoneInfo.name));
|
newRecords.push(recordsService.toDisplayRecord(record, $scope.zoneInfo.name));
|
||||||
});
|
});
|
||||||
|
angular.forEach(newRecords, function(record) {
|
||||||
|
if(record.ownerGroupId != undefined) {
|
||||||
|
$scope.recordSetGroupOwnerShipStatus(record.ownerGroupId, $scope.profile.id, record);
|
||||||
|
}else {record.isCurrentRecordSetOwner= null;}
|
||||||
|
});
|
||||||
$scope.records = newRecords;
|
$scope.records = newRecords;
|
||||||
$scope.getRecordSetCount();
|
$scope.getRecordSetCount();
|
||||||
if($scope.records.length > 0) {
|
if($scope.records.length > 0) {
|
||||||
|
@ -280,7 +280,10 @@ angular.module('service.records', [])
|
|||||||
"id": record.id,
|
"id": record.id,
|
||||||
"name": record.name,
|
"name": record.name,
|
||||||
"type": record.type,
|
"type": record.type,
|
||||||
"ttl": Number(record.ttl)
|
"ttl": Number(record.ttl),
|
||||||
|
"isCurrentRecordSetOwner": record.isCurrentRecordSetOwner,
|
||||||
|
"recordSetGroupChange": record.recordSetGroupChange
|
||||||
|
|
||||||
};
|
};
|
||||||
switch (record.type) {
|
switch (record.type) {
|
||||||
case 'A':
|
case 'A':
|
||||||
|
@ -111,7 +111,9 @@ describe('Service: recordsService', function () {
|
|||||||
"type": 'SSHFP',
|
"type": 'SSHFP',
|
||||||
"ttl": '300',
|
"ttl": '300',
|
||||||
"sshfpItems": [{algorithm: '1', type: '1', fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
"sshfpItems": [{algorithm: '1', type: '1', fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
||||||
{algorithm: '2', type: '1', fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'}]
|
{algorithm: '2', type: '1', fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'}],
|
||||||
|
"recordSetGroupChange": 'None',
|
||||||
|
"isCurrentRecordSetOwner": 'null'
|
||||||
};
|
};
|
||||||
expectedRecord = {
|
expectedRecord = {
|
||||||
"id": 'recordId',
|
"id": 'recordId',
|
||||||
@ -119,7 +121,9 @@ describe('Service: recordsService', function () {
|
|||||||
"type": 'SSHFP',
|
"type": 'SSHFP',
|
||||||
"ttl": 300,
|
"ttl": 300,
|
||||||
"records": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
"records": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
||||||
{algorithm: 2, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'}]
|
{algorithm: 2, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'}],
|
||||||
|
"recordSetGroupChange": 'None',
|
||||||
|
"isCurrentRecordSetOwner": 'null'
|
||||||
};
|
};
|
||||||
|
|
||||||
var actualRecord = this.recordsService.toVinylRecord(sentRecord);
|
var actualRecord = this.recordsService.toVinylRecord(sentRecord);
|
||||||
@ -133,7 +137,8 @@ describe('Service: recordsService', function () {
|
|||||||
"type": 'SSHFP',
|
"type": 'SSHFP',
|
||||||
"ttl": 300,
|
"ttl": 300,
|
||||||
"records": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
"records": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
||||||
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}]
|
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}],
|
||||||
|
"recordSetGroupChange": 'None'
|
||||||
};
|
};
|
||||||
|
|
||||||
displayRecord = {
|
displayRecord = {
|
||||||
@ -144,6 +149,7 @@ describe('Service: recordsService', function () {
|
|||||||
"records": undefined,
|
"records": undefined,
|
||||||
"sshfpItems": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
"sshfpItems": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
||||||
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}],
|
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}],
|
||||||
|
"recordSetGroupChange": 'None',
|
||||||
"onlyFour": true,
|
"onlyFour": true,
|
||||||
"isDotted": false,
|
"isDotted": false,
|
||||||
"canBeEdited": true
|
"canBeEdited": true
|
||||||
@ -160,7 +166,8 @@ describe('Service: recordsService', function () {
|
|||||||
"type": 'SSHFP',
|
"type": 'SSHFP',
|
||||||
"ttl": 300,
|
"ttl": 300,
|
||||||
"records": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
"records": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
||||||
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}]
|
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}],
|
||||||
|
"recordSetGroupChange": 'None'
|
||||||
};
|
};
|
||||||
|
|
||||||
displayRecord = {
|
displayRecord = {
|
||||||
@ -171,6 +178,7 @@ describe('Service: recordsService', function () {
|
|||||||
"records": undefined,
|
"records": undefined,
|
||||||
"sshfpItems": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
"sshfpItems": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
||||||
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}],
|
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}],
|
||||||
|
"recordSetGroupChange": 'None',
|
||||||
"onlyFour": true,
|
"onlyFour": true,
|
||||||
"isDotted": true,
|
"isDotted": true,
|
||||||
"canBeEdited": true
|
"canBeEdited": true
|
||||||
@ -187,7 +195,8 @@ describe('Service: recordsService', function () {
|
|||||||
"type": 'SSHFP',
|
"type": 'SSHFP',
|
||||||
"ttl": 300,
|
"ttl": 300,
|
||||||
"records": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
"records": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
||||||
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}]
|
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}],
|
||||||
|
"recordSetGroupChange": 'None'
|
||||||
};
|
};
|
||||||
|
|
||||||
displayRecord = {
|
displayRecord = {
|
||||||
@ -198,6 +207,7 @@ describe('Service: recordsService', function () {
|
|||||||
"records": undefined,
|
"records": undefined,
|
||||||
"sshfpItems": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
"sshfpItems": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
||||||
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}],
|
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}],
|
||||||
|
"recordSetGroupChange": 'None',
|
||||||
"onlyFour": true,
|
"onlyFour": true,
|
||||||
"isDotted": false,
|
"isDotted": false,
|
||||||
"canBeEdited": true
|
"canBeEdited": true
|
||||||
@ -213,7 +223,8 @@ describe('Service: recordsService', function () {
|
|||||||
"name": 'apex.with.dot.',
|
"name": 'apex.with.dot.',
|
||||||
"type": 'NS',
|
"type": 'NS',
|
||||||
"ttl": 300,
|
"ttl": 300,
|
||||||
"records": [{nsdname: "ns1.com."}, {nsdname: "ns2.com."}]
|
"records": [{nsdname: "ns1.com."}, {nsdname: "ns2.com."}],
|
||||||
|
"recordSetGroupChange": 'None'
|
||||||
};
|
};
|
||||||
|
|
||||||
displayRecord = {
|
displayRecord = {
|
||||||
@ -223,6 +234,7 @@ describe('Service: recordsService', function () {
|
|||||||
"ttl": 300,
|
"ttl": 300,
|
||||||
"records": undefined,
|
"records": undefined,
|
||||||
"nsRecordData": ["ns1.com.", "ns2.com."],
|
"nsRecordData": ["ns1.com.", "ns2.com."],
|
||||||
|
"recordSetGroupChange": 'None',
|
||||||
"onlyFour": true,
|
"onlyFour": true,
|
||||||
"isDotted": false,
|
"isDotted": false,
|
||||||
"canBeEdited": false
|
"canBeEdited": false
|
||||||
@ -239,7 +251,8 @@ describe('Service: recordsService', function () {
|
|||||||
"type": 'SSHFP',
|
"type": 'SSHFP',
|
||||||
"ttl": 300,
|
"ttl": 300,
|
||||||
"records": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
"records": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
||||||
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}]
|
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}],
|
||||||
|
"recordSetGroupChange": 'None'
|
||||||
};
|
};
|
||||||
|
|
||||||
displayRecord = {
|
displayRecord = {
|
||||||
@ -250,6 +263,7 @@ describe('Service: recordsService', function () {
|
|||||||
"records": undefined,
|
"records": undefined,
|
||||||
"sshfpItems": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
"sshfpItems": [{algorithm: 1, type: 1, fingerprint: '123456789ABCDEF67890123456789ABCDEF67890'},
|
||||||
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}],
|
{algorithm: 2, type: 1, fingerprint: 'F23456789ABCDEF67890123456789ABCDEF67890'}],
|
||||||
|
"recordSetGroupChange": 'None',
|
||||||
"onlyFour": true,
|
"onlyFour": true,
|
||||||
"isDotted": false,
|
"isDotted": false,
|
||||||
"canBeEdited": true
|
"canBeEdited": true
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
<div>
|
||||||
|
|
||||||
<form name="addRecordForm" role="form" class="form-horizontal" novalidate>
|
<form name="addRecordForm" role="form" class="form-horizontal" novalidate>
|
||||||
<modal modal-id="record_modal" modal-title="{{ recordModal.title }}">
|
<modal modal-id="record_modal" modal-title="{{ recordModal.title }}">
|
||||||
<modal-body>
|
<modal-body>
|
||||||
@ -468,10 +470,19 @@
|
|||||||
<option value="" selected="selected">Please choose a record owner group</option>
|
<option value="" selected="selected">Please choose a record owner group</option>
|
||||||
</select>
|
</select>
|
||||||
<modal-invalid>
|
<modal-invalid>
|
||||||
Record Owner Group is required for records in shared zones
|
Owner Group transfer status is required for records in shared zones
|
||||||
</modal-invalid>
|
</modal-invalid>
|
||||||
</modal-element>
|
</modal-element>
|
||||||
|
<modal-element label="Requested Record Owner Group"
|
||||||
|
invalid-when="addRecordForm.$submitted && addRecordForm.recordSetGroupChange.requestedOwnerGroupId.$invalid"
|
||||||
|
ng-if="recordModal.sharedDisplayEnabled && recordModal.sharedZone && recordModal.previous.recordSetGroupChange.ownerShipTransferStatus=='PendingReview'">
|
||||||
|
<input name="requestedOwnerGroupName"
|
||||||
|
class="form-control"
|
||||||
|
ng-model="recordSetRequestedOwnerShipName"
|
||||||
|
ng-class="recordModal.details.class"
|
||||||
|
ng-readonly="recordModal.basics.readOnly"
|
||||||
|
placeholder="">
|
||||||
|
</modal-element>
|
||||||
</modal-body>
|
</modal-body>
|
||||||
<modal-footer>
|
<modal-footer>
|
||||||
<span ng-if="recordModal.action == recordModalState.CREATE">
|
<span ng-if="recordModal.action == recordModalState.CREATE">
|
||||||
@ -506,3 +517,97 @@
|
|||||||
</modal-footer>
|
</modal-footer>
|
||||||
</modal>
|
</modal>
|
||||||
</form>
|
</form>
|
||||||
|
<form name="recordOwnerShipForm" role="form" class="form-horizontal" novalidate>
|
||||||
|
<!--Request ownership for unowned records in shared zones-->
|
||||||
|
<modal modal-id="record_modal_ownership" modal-title="Request OwnerShip">
|
||||||
|
<modal-body>
|
||||||
|
<modal-element label="Record Owner Group Request"
|
||||||
|
invalid-when="recordOwnerShipForm.$submitted && recordOwnerShipForm.requestOwnerGroupId.$invalid"
|
||||||
|
ng-if = "recordModal.sharedDisplayEnabled && recordModal.sharedZone && !recordModal.isCurrentRecordOwnerGroup && currentRecord.ownerGroupId == undefined">
|
||||||
|
<select name="requestOwnerGroupId"
|
||||||
|
class="form-control"
|
||||||
|
ng-model="currentRecord.recordSetGroupChange.requestedOwnerGroupId"
|
||||||
|
ng-disabled="recordModal.details.readOnly"
|
||||||
|
ng-class="recordModal.details.class"
|
||||||
|
ng-options="group.id as group.name for group in myGroups | orderBy: 'name'"
|
||||||
|
required>
|
||||||
|
<option value="" selected="selected">Please choose a record owner group</option>
|
||||||
|
</select>
|
||||||
|
<modal-invalid>
|
||||||
|
Requesting Record Owner Group is required for records in shared zones
|
||||||
|
</modal-invalid>
|
||||||
|
</modal-element>
|
||||||
|
</modal-body>
|
||||||
|
<modal-footer>
|
||||||
|
<span>
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal" ng-click="closeRecordModal()">Close</button>
|
||||||
|
<button class="btn btn-primary pull-right" ng-click="requestedOwnerShip()" id="requested_ownership">Request</button>
|
||||||
|
</span>
|
||||||
|
</modal-footer>
|
||||||
|
</modal>
|
||||||
|
<!--Request and approve ownership for owned records in shared zones-->
|
||||||
|
<modal modal-id="record_modal_ownership_transfer" modal-title="OwnerShip Transfer">
|
||||||
|
<modal-body>
|
||||||
|
<modal-element label="Record Owner Group Request"
|
||||||
|
invalid-when="recordOwnerShipForm.$submitted && recordOwnerShipForm.requestedOwnerGroupId1.$invalid"
|
||||||
|
ng-if="recordModal.sharedDisplayEnabled && recordModal.sharedZone"
|
||||||
|
ng-switch="currentOwnerShipTransferApprover || recordModal.previous.recordSetGroupChange.ownerShipTransferStatus == 'PendingReview'">
|
||||||
|
<select name="requestedOwnerGroupId1"
|
||||||
|
class="form-control"
|
||||||
|
ng-model="currentRecord.recordSetGroupChange.requestedOwnerGroupId"
|
||||||
|
ng-switch-when="false"
|
||||||
|
ng-disabled="recordModal.details.readOnly"
|
||||||
|
ng-class="recordModal.details.class"
|
||||||
|
ng-options="group.id as group.name for group in myGroups | orderBy: 'name'"
|
||||||
|
required>
|
||||||
|
<option value="" selected="selected">Please choose a record owner group</option>
|
||||||
|
</select>
|
||||||
|
<modal-invalid>
|
||||||
|
Record Owner Group is required for records in shared zones
|
||||||
|
</modal-invalid>
|
||||||
|
<input name="requestedOwnerGroupId2"
|
||||||
|
class="form-control"
|
||||||
|
ng-switch-default
|
||||||
|
ng-model="recordSetRequestedOwnerShipName"
|
||||||
|
ng-class="recordModal.details.class"
|
||||||
|
ng-readonly="recordModal.basics.readOnly"
|
||||||
|
>
|
||||||
|
</modal-element>
|
||||||
|
|
||||||
|
<modal-element label="Record Owner Group Status"
|
||||||
|
invalid-when="recordOwnerShipForm.$submitted && (recordOwnerShipForm.ownerGroupStatus.$invalid
|
||||||
|
|| recordOwnerShipForm.ownerGroupStatus.$viewValue == null || !recordOwnerShipForm.ownerGroupStatus.$viewValue)"
|
||||||
|
ng-if="recordModal.sharedDisplayEnabled && recordModal.sharedZone "
|
||||||
|
ng-switch="!currentOwnerShipTransferApprover">
|
||||||
|
<select name="ownerGroupStatus"
|
||||||
|
class="form-control"
|
||||||
|
ng-switch-when="false"
|
||||||
|
ng-model="currentRecord.recordSetGroupChange.ownerShipTransferStatus"
|
||||||
|
ng-class="recordModal.details.class"
|
||||||
|
ng-options="ownerShipTransferApproverStatus.value as ownerShipTransferApproverStatus.label for ownerShipTransferApproverStatus in ownerShipTransferStatus" required>
|
||||||
|
<option value= "" selected="selected">Please choose a record owner group status</option>
|
||||||
|
|
||||||
|
</select>
|
||||||
|
<select name="ownerGroupStatus"
|
||||||
|
class="form-control"
|
||||||
|
ng-switch-default
|
||||||
|
ng-model="currentRecord.recordSetGroupChange.ownerShipTransferStatus"
|
||||||
|
ng-class="recordModal.details.class">
|
||||||
|
<option value= "" selected="selected">Please choose a record owner group status</option>
|
||||||
|
<option ng-if="recordModal.previous.recordSetGroupChange.ownerShipTransferStatus == 'PendingReview'" value="Cancelled" required>Cancel</option>
|
||||||
|
<option ng-if="recordModal.previous.recordSetGroupChange.ownerShipTransferStatus != 'PendingReview'" value="Requested" required>Request</option>
|
||||||
|
</select>
|
||||||
|
<modal-invalid>
|
||||||
|
Owner Group transfer status is required for records in shared zones
|
||||||
|
</modal-invalid>
|
||||||
|
</modal-element>
|
||||||
|
</modal-body>
|
||||||
|
<modal-footer>
|
||||||
|
<span>
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal" ng-click="closeRecordModal()">Close</button>
|
||||||
|
<button class="btn btn-primary pull-right" ng-click="submitRequestedOwnerShipTransfer()" id="update_recordset_ownership_request">Submit</button>
|
||||||
|
</span>
|
||||||
|
</modal-footer>
|
||||||
|
</modal>
|
||||||
|
</form>
|
||||||
|
</div>
|
@ -296,6 +296,7 @@ trait TestApplicationData { this: Mockito =>
|
|||||||
| "ttl": "200",
|
| "ttl": "200",
|
||||||
| "status": "${RecordSetStatus.Active}",
|
| "status": "${RecordSetStatus.Active}",
|
||||||
| "records": [ { "address": "10.1.1.1" } ],
|
| "records": [ { "address": "10.1.1.1" } ],
|
||||||
|
| "recordSetGroupChange": "None",
|
||||||
| "id": "$hobbitRecordSetId"
|
| "id": "$hobbitRecordSetId"
|
||||||
| }
|
| }
|
||||||
""".stripMargin)
|
""".stripMargin)
|
||||||
|
@ -25,7 +25,7 @@ import com.amazonaws.services.route53.model.{
|
|||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import vinyldns.core.domain.Fqdn
|
import vinyldns.core.domain.Fqdn
|
||||||
import vinyldns.core.domain.record.{NSData, RecordData, RecordSet, RecordSetStatus, RecordType}
|
import vinyldns.core.domain.record.{NSData, OwnerShipTransfer, OwnerShipTransferStatus, RecordData, RecordSet, RecordSetStatus, RecordType}
|
||||||
import vinyldns.core.domain.record.RecordType.RecordType
|
import vinyldns.core.domain.record.RecordType.RecordType
|
||||||
import vinyldns.core.domain.record.RecordType._
|
import vinyldns.core.domain.record.RecordType._
|
||||||
import vinyldns.core.domain.zone.Zone
|
import vinyldns.core.domain.zone.Zone
|
||||||
@ -82,6 +82,7 @@ trait Route53Conversions {
|
|||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
Some(Instant.now.truncatedTo(ChronoUnit.MILLIS)),
|
Some(Instant.now.truncatedTo(ChronoUnit.MILLIS)),
|
||||||
r53RecordSet.getResourceRecords.asScala.toList.flatMap(toVinyl(typ, _)),
|
r53RecordSet.getResourceRecords.asScala.toList.flatMap(toVinyl(typ, _)),
|
||||||
|
recordSetGroupChange=Some(OwnerShipTransfer(ownerShipTransferStatus = OwnerShipTransferStatus.AutoApproved)),
|
||||||
fqdn = Some(r53RecordSet.getName)
|
fqdn = Some(r53RecordSet.getName)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -110,6 +111,7 @@ trait Route53Conversions {
|
|||||||
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
Instant.now.truncatedTo(ChronoUnit.MILLIS),
|
||||||
Some(Instant.now.truncatedTo(ChronoUnit.MILLIS)),
|
Some(Instant.now.truncatedTo(ChronoUnit.MILLIS)),
|
||||||
nsData,
|
nsData,
|
||||||
|
recordSetGroupChange = Some(OwnerShipTransfer(ownerShipTransferStatus = OwnerShipTransferStatus.AutoApproved)),
|
||||||
fqdn = Some(Fqdn(zoneName).fqdn)
|
fqdn = Some(Fqdn(zoneName).fqdn)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user