2
0
mirror of https://github.com/VinylDNS/vinyldns synced 2025-08-31 14:25:30 +00:00

add history

This commit is contained in:
Aravindh-Raju
2023-04-05 18:23:32 +05:30
parent 19ba3c09fe
commit f103d2c6b8
8 changed files with 125 additions and 25 deletions

View File

@@ -59,7 +59,6 @@ class ZoneViewLoaderIntegrationSpec extends AnyWordSpec with Matchers {
Some(ZoneConnection("invalid-connection.", "bad-key", Encrypted("invalid-key"), "10.1.1.1"))
)
val backend = backendResolver.resolve(zone).asInstanceOf[DnsBackend]
println(s"${backend.id}, ${backend.xfrInfo}, ${backend.resolver.getAddress}")
DnsZoneViewLoader(zone, backendResolver.resolve(zone), 10000)
.load()
.unsafeRunSync()

View File

@@ -92,10 +92,12 @@ class RecordSetService(
for {
zone <- getZone(recordSet.zoneId)
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
rsForValidations = change.recordSet
_ <- isNotHighValueDomain(recordSet, zone, highValueDomainConfig).toResult
_ <- isNotHighValueDomain(newRecordSet, zone, highValueDomainConfig).toResult
_ <- recordSetDoesNotExist(
backendResolver.resolve,
zone,
@@ -142,11 +144,13 @@ class RecordSetService(
_ <- unchangedRecordName(existing, recordSet, zone).toResult
_ <- unchangedRecordType(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
rsForValidations = change.recordSet
superUserCanUpdateOwnerGroup = canSuperUserUpdateOwnerGroup(existing, recordSet, zone, auth)
_ <- isNotHighValueDomain(recordSet, zone, highValueDomainConfig).toResult
superUserCanUpdateOwnerGroup = canSuperUserUpdateOwnerGroup(existing, newRecordSet, zone, auth)
_ <- isNotHighValueDomain(newRecordSet, zone, highValueDomainConfig).toResult
_ <- canUpdateRecordSet(auth, existing.name, existing.typ, zone, existing.ownerGroupId, superUserCanUpdateOwnerGroup).toResult
ownerGroup <- getGroupIfProvided(rsForValidations.ownerGroupId)
_ <- canUseOwnerGroup(rsForValidations.ownerGroupId, ownerGroup, auth).toResult
@@ -575,7 +579,6 @@ class RecordSetService(
recordType: Option[RecordType] = None,
authPrincipal: AuthPrincipal
): Result[ListRecordSetChangesResponse] = {
println("In listRecordSetChanges")
if(zoneId.isDefined) {
for {
zone <- getZone(zoneId.get)

View File

@@ -108,7 +108,11 @@ object ZoneSyncHandler extends DnsConversions with Monitored with TransactionPro
case (dnsZoneView, vinylDnsZoneView) => vinylDnsZoneView.diff(dnsZoneView)
}
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) {
logger.info(

View File

@@ -76,6 +76,12 @@ case class RecordSetChange(
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 = {
val sb = new StringBuilder
sb.append("RecordSetChange: [")

View File

@@ -89,9 +89,6 @@ class MySqlRecordChangeRepository
*/
def save(db: DB, changeSet: ChangeSet): IO[ChangeSet] =
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 {
db.withinTx { implicit session =>
changeSet.changes

View File

@@ -76,7 +76,8 @@ class FrontendController @Inject() (
}
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 =>

View File

@@ -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 = {
<!-- PAGE CONTENT -->
@@ -80,8 +80,8 @@
<div class="vinyldns-panel-top">
<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="create-record-button" class="btn btn-default" 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="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" ng-if="zoneInfo.accessLevel=='Delete'" data-toggle="modal" data-target="#mb-sync"><span class="fa fa-exchange"></span> Sync Zone</button>
</div>
<div>
@@ -164,7 +164,9 @@
@if(meta.sharedDisplayEnabled) {
<th>Owner Group Name</th>
}
@if(rootAccountCanReview) {
<th>Record History</th>
}
</tr>
</thead>
<tbody>
@@ -372,9 +374,11 @@
title="Group with ID {{record.ownerGroupId}} no longer exists."><span class="fa fa-warning"></span> Group deleted</span>
</td>
}
@if(rootAccountCanReview) {
<td>
<span><button class="btn btn-info btn-sm" ng-click="viewRecordHistory(record.fqdn, record.type)">View History</button></span>
</td>
}
</tr>
</tbody>
</table>
@@ -420,7 +424,7 @@
</div>
<div class="panel-body">
<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>
<!-- PAGINATION -->
@@ -460,13 +464,13 @@
</td>
<td class="col-md-3 wrap-long-text">
{{change.systemMessage}}
<!-- <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 =='Delete'" ng-click="viewRecordInfo(change.recordSet)" class="force-cursor">View deleted recordset</a>-->
<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 =='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.updates)" class="force-cursor">View old recordset</a></div>-->
<!-- </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>
</td>
</tr>
</tbody>
@@ -497,6 +501,8 @@
</div>
</div>
</div>
<recordmodal></recordmodal>
}
@plugins = {

View File

@@ -30,6 +30,45 @@
$scope.groups = [];
$scope.recordFqdn = 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
var changePaging = pagingService.getNewPagingParams(100);
@@ -73,8 +112,6 @@
.appendTo(ul); };
$scope.viewRecordHistory = function(recordFqdn, recordType) {
$log.log("recordFqdn: ", recordFqdn);
$log.log("recordType: ", recordType);
$scope.recordFqdn = recordFqdn;
$scope.recordType = recordType;
$scope.refreshRecordChangeHistory($scope.recordFqdn, $scope.recordType);
@@ -93,6 +130,7 @@
function success(response) {
recordsPaging.next = response.data.nextId;
updateRecordDisplay(response.data['recordSets']);
getMembership();
}
return recordsService
.listRecordSetData(recordsPaging.maxItems, undefined, recordName, recordType, $scope.nameSort, $scope.ownerGroupFilter)
@@ -201,7 +239,8 @@
$scope.refreshRecordChangeHistory = function(recordFqdn, recordType) {
changePaging = pagingService.resetPaging(changePaging);
function success(response) {
$log.log('recordsService::getRecordSetChangeHistory-success');
$scope.zoneId = response.data.zoneId;
$scope.refreshZone();
changePaging.next = response.data.nextId;
updateChangeDisplay(response.data.recordSetChanges)
}
@@ -272,5 +311,50 @@
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;
};
});
})();