mirror of
https://github.com/VinylDNS/vinyldns
synced 2025-09-04 16:25:32 +00:00
add history
This commit is contained in:
@@ -59,7 +59,6 @@ class ZoneViewLoaderIntegrationSpec extends AnyWordSpec with Matchers {
|
|||||||
Some(ZoneConnection("invalid-connection.", "bad-key", Encrypted("invalid-key"), "10.1.1.1"))
|
Some(ZoneConnection("invalid-connection.", "bad-key", Encrypted("invalid-key"), "10.1.1.1"))
|
||||||
)
|
)
|
||||||
val backend = backendResolver.resolve(zone).asInstanceOf[DnsBackend]
|
val backend = backendResolver.resolve(zone).asInstanceOf[DnsBackend]
|
||||||
println(s"${backend.id}, ${backend.xfrInfo}, ${backend.resolver.getAddress}")
|
|
||||||
DnsZoneViewLoader(zone, backendResolver.resolve(zone), 10000)
|
DnsZoneViewLoader(zone, backendResolver.resolve(zone), 10000)
|
||||||
.load()
|
.load()
|
||||||
.unsafeRunSync()
|
.unsafeRunSync()
|
||||||
|
@@ -92,10 +92,12 @@ class RecordSetService(
|
|||||||
for {
|
for {
|
||||||
zone <- getZone(recordSet.zoneId)
|
zone <- getZone(recordSet.zoneId)
|
||||||
authZones = dottedHostsConfig.zoneAuthConfigs.map(x => x.zone)
|
authZones = dottedHostsConfig.zoneAuthConfigs.map(x => x.zone)
|
||||||
change <- RecordSetChangeGenerator.forAdd(recordSet, zone, Some(auth)).toResult
|
isShared = zone.shared
|
||||||
|
newRecordSet = if(isShared) recordSet else recordSet.copy(ownerGroupId = Some(zone.adminGroupId))
|
||||||
|
change <- RecordSetChangeGenerator.forAdd(newRecordSet, zone, Some(auth)).toResult
|
||||||
// because changes happen to the RS in forAdd itself, converting 1st and validating on that
|
// because changes happen to the RS in forAdd itself, converting 1st and validating on that
|
||||||
rsForValidations = change.recordSet
|
rsForValidations = change.recordSet
|
||||||
_ <- isNotHighValueDomain(recordSet, zone, highValueDomainConfig).toResult
|
_ <- isNotHighValueDomain(newRecordSet, zone, highValueDomainConfig).toResult
|
||||||
_ <- recordSetDoesNotExist(
|
_ <- recordSetDoesNotExist(
|
||||||
backendResolver.resolve,
|
backendResolver.resolve,
|
||||||
zone,
|
zone,
|
||||||
@@ -142,11 +144,13 @@ 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
|
||||||
change <- RecordSetChangeGenerator.forUpdate(existing, recordSet, zone, Some(auth)).toResult
|
isShared = zone.shared
|
||||||
|
newRecordSet = if(isShared) recordSet else recordSet.copy(ownerGroupId = Some(zone.adminGroupId))
|
||||||
|
change <- RecordSetChangeGenerator.forUpdate(existing, newRecordSet, 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, newRecordSet, zone, auth)
|
||||||
_ <- isNotHighValueDomain(recordSet, zone, highValueDomainConfig).toResult
|
_ <- isNotHighValueDomain(newRecordSet, zone, highValueDomainConfig).toResult
|
||||||
_ <- canUpdateRecordSet(auth, existing.name, existing.typ, zone, existing.ownerGroupId, superUserCanUpdateOwnerGroup).toResult
|
_ <- 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
|
_ <- canUseOwnerGroup(rsForValidations.ownerGroupId, ownerGroup, auth).toResult
|
||||||
@@ -575,7 +579,6 @@ class RecordSetService(
|
|||||||
recordType: Option[RecordType] = None,
|
recordType: Option[RecordType] = None,
|
||||||
authPrincipal: AuthPrincipal
|
authPrincipal: AuthPrincipal
|
||||||
): Result[ListRecordSetChangesResponse] = {
|
): Result[ListRecordSetChangesResponse] = {
|
||||||
println("In listRecordSetChanges")
|
|
||||||
if(zoneId.isDefined) {
|
if(zoneId.isDefined) {
|
||||||
for {
|
for {
|
||||||
zone <- getZone(zoneId.get)
|
zone <- getZone(zoneId.get)
|
||||||
|
@@ -108,7 +108,11 @@ object ZoneSyncHandler extends DnsConversions with Monitored with TransactionPro
|
|||||||
case (dnsZoneView, vinylDnsZoneView) => vinylDnsZoneView.diff(dnsZoneView)
|
case (dnsZoneView, vinylDnsZoneView) => vinylDnsZoneView.diff(dnsZoneView)
|
||||||
}
|
}
|
||||||
recordSetChanges.flatMap { allChanges =>
|
recordSetChanges.flatMap { allChanges =>
|
||||||
val changesWithUserIds = allChanges.map(_.withUserId(zoneChange.userId))
|
val changesWithUserIds = if(zone.shared) {
|
||||||
|
allChanges.map(_.withUserId(zoneChange.userId))
|
||||||
|
} else {
|
||||||
|
allChanges.map(_.withUserId(zoneChange.userId)).map(_.withGroupId(Some(zone.adminGroupId)))
|
||||||
|
}
|
||||||
|
|
||||||
if (changesWithUserIds.isEmpty) {
|
if (changesWithUserIds.isEmpty) {
|
||||||
logger.info(
|
logger.info(
|
||||||
|
@@ -76,6 +76,12 @@ case class RecordSetChange(
|
|||||||
|
|
||||||
def withUserId(newUserId: String): RecordSetChange = this.copy(userId = newUserId)
|
def withUserId(newUserId: String): RecordSetChange = this.copy(userId = newUserId)
|
||||||
|
|
||||||
|
def withGroupId(adminGroupId: Option[String]): RecordSetChange =
|
||||||
|
copy(
|
||||||
|
recordSet = recordSet
|
||||||
|
.copy(ownerGroupId = adminGroupId)
|
||||||
|
)
|
||||||
|
|
||||||
override def toString: String = {
|
override def toString: String = {
|
||||||
val sb = new StringBuilder
|
val sb = new StringBuilder
|
||||||
sb.append("RecordSetChange: [")
|
sb.append("RecordSetChange: [")
|
||||||
|
@@ -89,9 +89,6 @@ class MySqlRecordChangeRepository
|
|||||||
*/
|
*/
|
||||||
def save(db: DB, changeSet: ChangeSet): IO[ChangeSet] =
|
def save(db: DB, changeSet: ChangeSet): IO[ChangeSet] =
|
||||||
monitor("repo.RecordChange.save") {
|
monitor("repo.RecordChange.save") {
|
||||||
println("fqdn: ", changeSet.changes.map(x => x.recordSet.fqdn))
|
|
||||||
println("name: ", changeSet.changes.map(x => x.recordSet.name))
|
|
||||||
println("zone: ", changeSet.changes.map(x => x.zone.name))
|
|
||||||
IO {
|
IO {
|
||||||
db.withinTx { implicit session =>
|
db.withinTx { implicit session =>
|
||||||
changeSet.changes
|
changeSet.changes
|
||||||
|
@@ -76,7 +76,8 @@ class FrontendController @Inject() (
|
|||||||
}
|
}
|
||||||
|
|
||||||
def viewRecordSets(): Action[AnyContent] = userAction.async { implicit request =>
|
def viewRecordSets(): Action[AnyContent] = userAction.async { implicit request =>
|
||||||
Future(Ok(views.html.recordsets.recordSets(request.user.userName)))
|
val canReview = request.user.isSuper || request.user.isSupport
|
||||||
|
Future(Ok(views.html.recordsets.recordSets(request.user.userName, canReview)))
|
||||||
}
|
}
|
||||||
|
|
||||||
def viewAllBatchChanges(): Action[AnyContent] = userAction.async { implicit request =>
|
def viewAllBatchChanges(): Action[AnyContent] = userAction.async { implicit request =>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
@(rootAccountName: String)(implicit request: play.api.mvc.Request[Any], customLinks: models.CustomLinks, meta: models.Meta)
|
@(rootAccountName: String, rootAccountCanReview: Boolean)(implicit request: play.api.mvc.Request[Any], customLinks: models.CustomLinks, meta: models.Meta)
|
||||||
|
|
||||||
@content = {
|
@content = {
|
||||||
<!-- PAGE CONTENT -->
|
<!-- PAGE CONTENT -->
|
||||||
@@ -80,8 +80,8 @@
|
|||||||
<div class="vinyldns-panel-top">
|
<div class="vinyldns-panel-top">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button id="refresh-records-button" class="btn btn-default" ng-click="refreshRecords()"><span class="fa fa-refresh"></span> Refresh</button>
|
<button id="refresh-records-button" class="btn btn-default" ng-click="refreshRecords()"><span class="fa fa-refresh"></span> Refresh</button>
|
||||||
<button id="create-record-button" class="btn btn-default" ng-click="createRecord(defaultTtl)"><span class="fa fa-plus"></span> Create Record Set</button>
|
<button id="create-record-button" class="btn btn-default" ng-if="canCreateRecords" ng-click="createRecord(defaultTtl)"><span class="fa fa-plus"></span> Create Record Set</button>
|
||||||
<button id="zone-sync-button" class="btn btn-default mb-control" data-toggle="modal" data-target="#mb-sync"><span class="fa fa-exchange"></span> Sync Zone</button>
|
<button id="zone-sync-button" class="btn btn-default mb-control" ng-if="zoneInfo.accessLevel=='Delete'" data-toggle="modal" data-target="#mb-sync"><span class="fa fa-exchange"></span> Sync Zone</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -164,7 +164,9 @@
|
|||||||
@if(meta.sharedDisplayEnabled) {
|
@if(meta.sharedDisplayEnabled) {
|
||||||
<th>Owner Group Name</th>
|
<th>Owner Group Name</th>
|
||||||
}
|
}
|
||||||
|
@if(rootAccountCanReview) {
|
||||||
<th>Record History</th>
|
<th>Record History</th>
|
||||||
|
}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -372,9 +374,11 @@
|
|||||||
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>
|
||||||
}
|
}
|
||||||
|
@if(rootAccountCanReview) {
|
||||||
<td>
|
<td>
|
||||||
<span><button class="btn btn-info btn-sm" ng-click="viewRecordHistory(record.fqdn, record.type)">View History</button></span>
|
<span><button class="btn btn-info btn-sm" ng-click="viewRecordHistory(record.fqdn, record.type)">View History</button></span>
|
||||||
</td>
|
</td>
|
||||||
|
}
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -420,7 +424,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-default" ng-click="refreshRecordChangeHistory()"><span class="fa fa-refresh"></span>Refresh</button>
|
<button class="btn btn-default" ng-click="refreshRecordChangeHistory(recordFqdn, recordType)"><span class="fa fa-refresh"></span> Refresh</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- PAGINATION -->
|
<!-- PAGINATION -->
|
||||||
@@ -460,13 +464,13 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="col-md-3 wrap-long-text">
|
<td class="col-md-3 wrap-long-text">
|
||||||
{{change.systemMessage}}
|
{{change.systemMessage}}
|
||||||
<!-- <div ng-if="change.status !='Failed'">-->
|
<div ng-if="change.status !='Failed'">
|
||||||
<!-- <a ng-if="change.changeType =='Create'" ng-click="viewRecordInfo(change.recordSet)" class="force-cursor">View created recordset</a>-->
|
<a ng-if="change.changeType =='Create'" ng-click="viewRecordInfo(change.recordSet)" class="force-cursor">View created recordset</a>
|
||||||
<!-- <a ng-if="change.changeType =='Delete'" ng-click="viewRecordInfo(change.recordSet)" class="force-cursor">View deleted recordset</a>-->
|
<a ng-if="change.changeType =='Delete'" ng-click="viewRecordInfo(change.recordSet)" class="force-cursor">View deleted recordset</a>
|
||||||
|
|
||||||
<!-- <div><a ng-if="change.changeType =='Update'" ng-click="viewRecordInfo(change.recordSet)" class="force-cursor">View new recordset</a></div>-->
|
<div><a ng-if="change.changeType =='Update'" ng-click="viewRecordInfo(change.recordSet)" class="force-cursor">View new recordset</a></div>
|
||||||
<!-- <div><a ng-if="change.changeType =='Update'" ng-click="viewRecordInfo(change.updates)" class="force-cursor">View old recordset</a></div>-->
|
<div><a ng-if="change.changeType =='Update'" ng-click="viewRecordInfo(change.updates)" class="force-cursor">View old recordset</a></div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -497,6 +501,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<recordmodal></recordmodal>
|
||||||
}
|
}
|
||||||
|
|
||||||
@plugins = {
|
@plugins = {
|
||||||
|
@@ -30,6 +30,45 @@
|
|||||||
$scope.groups = [];
|
$scope.groups = [];
|
||||||
$scope.recordFqdn = undefined;
|
$scope.recordFqdn = undefined;
|
||||||
$scope.recordType = undefined;
|
$scope.recordType = undefined;
|
||||||
|
$scope.recordsetChanges = {};
|
||||||
|
$scope.currentRecord = {};
|
||||||
|
$scope.zoneInfo = {};
|
||||||
|
$scope.profile = {};
|
||||||
|
$scope.recordTypes = ['A', 'AAAA', 'CNAME', 'DS', 'MX', 'NS', 'PTR', 'SRV', 'NAPTR', 'SSHFP', 'TXT', 'SOA'];
|
||||||
|
$scope.sshfpAlgorithms = [{name: '(1) RSA', number: 1}, {name: '(2) DSA', number: 2}, {name: '(3) ECDSA', number: 3},
|
||||||
|
{name: '(4) Ed25519', number: 4}];
|
||||||
|
$scope.sshfpTypes = [{name: '(1) SHA-1', number: 1}, {name: '(2) SHA-256', number: 2}];
|
||||||
|
$scope.dsAlgorithms = [{name: '(3) DSA', number: 3}, {name: '(5) RSASHA1', number: 5},
|
||||||
|
{name: '(6) DSA_NSEC3_SHA1', number: 6}, {name: '(7) RSASHA1_NSEC3_SHA1' , number: 7},
|
||||||
|
{name: '(8) RSASHA256', number: 8}, {name: '(10) RSASHA512' , number: 10},
|
||||||
|
{name: '(12) ECC_GOST', number: 12}, {name: '(13) ECDSAP256SHA256' , number: 13},
|
||||||
|
{name: '(14) ECDSAP384SHA384', number: 14}, {name: '(15) ED25519', number: 15},
|
||||||
|
{name: '(16) ED448', number: 16},{name: '(253) PRIVATEDNS', number: 253},
|
||||||
|
{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.isZoneAdmin = false;
|
||||||
|
$scope.canReadZone = false;
|
||||||
|
$scope.canCreateRecords = false;
|
||||||
|
$scope.zoneId = undefined;
|
||||||
|
$scope.recordModalState = {
|
||||||
|
CREATE: 0,
|
||||||
|
UPDATE: 1,
|
||||||
|
DELETE: 2,
|
||||||
|
CONFIRM_UPDATE: 3,
|
||||||
|
CONFIRM_DELETE: 4,
|
||||||
|
VIEW_DETAILS: 5
|
||||||
|
};
|
||||||
|
// read-only data for setting various classes/attributes in record modal
|
||||||
|
$scope.recordModalParams = {
|
||||||
|
readOnly: {
|
||||||
|
class: "",
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
editable: {
|
||||||
|
class: "record-edit",
|
||||||
|
readOnly: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// paging status for record changes
|
// paging status for record changes
|
||||||
var changePaging = pagingService.getNewPagingParams(100);
|
var changePaging = pagingService.getNewPagingParams(100);
|
||||||
@@ -73,8 +112,6 @@
|
|||||||
.appendTo(ul); };
|
.appendTo(ul); };
|
||||||
|
|
||||||
$scope.viewRecordHistory = function(recordFqdn, recordType) {
|
$scope.viewRecordHistory = function(recordFqdn, recordType) {
|
||||||
$log.log("recordFqdn: ", recordFqdn);
|
|
||||||
$log.log("recordType: ", recordType);
|
|
||||||
$scope.recordFqdn = recordFqdn;
|
$scope.recordFqdn = recordFqdn;
|
||||||
$scope.recordType = recordType;
|
$scope.recordType = recordType;
|
||||||
$scope.refreshRecordChangeHistory($scope.recordFqdn, $scope.recordType);
|
$scope.refreshRecordChangeHistory($scope.recordFqdn, $scope.recordType);
|
||||||
@@ -93,6 +130,7 @@
|
|||||||
function success(response) {
|
function success(response) {
|
||||||
recordsPaging.next = response.data.nextId;
|
recordsPaging.next = response.data.nextId;
|
||||||
updateRecordDisplay(response.data['recordSets']);
|
updateRecordDisplay(response.data['recordSets']);
|
||||||
|
getMembership();
|
||||||
}
|
}
|
||||||
return recordsService
|
return recordsService
|
||||||
.listRecordSetData(recordsPaging.maxItems, undefined, recordName, recordType, $scope.nameSort, $scope.ownerGroupFilter)
|
.listRecordSetData(recordsPaging.maxItems, undefined, recordName, recordType, $scope.nameSort, $scope.ownerGroupFilter)
|
||||||
@@ -201,7 +239,8 @@
|
|||||||
$scope.refreshRecordChangeHistory = function(recordFqdn, recordType) {
|
$scope.refreshRecordChangeHistory = function(recordFqdn, recordType) {
|
||||||
changePaging = pagingService.resetPaging(changePaging);
|
changePaging = pagingService.resetPaging(changePaging);
|
||||||
function success(response) {
|
function success(response) {
|
||||||
$log.log('recordsService::getRecordSetChangeHistory-success');
|
$scope.zoneId = response.data.zoneId;
|
||||||
|
$scope.refreshZone();
|
||||||
changePaging.next = response.data.nextId;
|
changePaging.next = response.data.nextId;
|
||||||
updateChangeDisplay(response.data.recordSetChanges)
|
updateChangeDisplay(response.data.recordSetChanges)
|
||||||
}
|
}
|
||||||
@@ -272,5 +311,50 @@
|
|||||||
return 'info';
|
return 'info';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.viewRecordInfo = function(record) {
|
||||||
|
$scope.currentRecord = recordsService.toDisplayRecord(record);
|
||||||
|
$scope.recordModal = {
|
||||||
|
action: $scope.recordModalState.VIEW_DETAILS,
|
||||||
|
title: "Record Info",
|
||||||
|
basics: $scope.recordModalParams.readOnly,
|
||||||
|
details: $scope.recordModalParams.readOnly,
|
||||||
|
sharedZone: $scope.zoneInfo.shared,
|
||||||
|
sharedDisplayEnabled: $scope.sharedDisplayEnabled
|
||||||
|
};
|
||||||
|
$("#record_modal").modal("show");
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.refreshZone = function() {
|
||||||
|
function success(response) {
|
||||||
|
$log.debug('recordsService::getZone-success');
|
||||||
|
$scope.zoneInfo = response.data.zone;
|
||||||
|
// Get current user's groups and determine if they're an admin of this zone
|
||||||
|
getMembership()
|
||||||
|
}
|
||||||
|
return recordsService
|
||||||
|
.getZone($scope.zoneId)
|
||||||
|
.then(success)
|
||||||
|
.catch(function (error){
|
||||||
|
handleError(error, 'recordsService::getZone-catch');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function getMembership(){
|
||||||
|
groupsService
|
||||||
|
.getGroupsStored()
|
||||||
|
.then(
|
||||||
|
function (results) {
|
||||||
|
$scope.myGroups = results.groups;
|
||||||
|
$scope.myGroupIds = results.groups.map(function(grp) {return grp['id']});
|
||||||
|
})
|
||||||
|
.catch(function (error){
|
||||||
|
handleError(error, 'groupsService::getGroupsStored-failure');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.canAccessGroup = function(groupId) {
|
||||||
|
return $scope.myGroupIds !== undefined && $scope.myGroupIds.indexOf(groupId) > -1;
|
||||||
|
};
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
Reference in New Issue
Block a user