mirror of
https://github.com/VinylDNS/vinyldns
synced 2025-08-31 14:25:30 +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"))
|
||||
)
|
||||
val backend = backendResolver.resolve(zone).asInstanceOf[DnsBackend]
|
||||
println(s"${backend.id}, ${backend.xfrInfo}, ${backend.resolver.getAddress}")
|
||||
DnsZoneViewLoader(zone, backendResolver.resolve(zone), 10000)
|
||||
.load()
|
||||
.unsafeRunSync()
|
||||
|
@@ -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)
|
||||
|
@@ -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(
|
||||
|
@@ -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: [")
|
||||
|
@@ -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
|
||||
|
@@ -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 =>
|
||||
|
@@ -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 = {
|
||||
|
@@ -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;
|
||||
};
|
||||
});
|
||||
})();
|
||||
|
Reference in New Issue
Block a user