Vulnerability UI part 2

Fixes #860
Fixes #855
This commit is contained in:
Joseph Schorr 2015-11-12 15:42:45 -05:00
parent 6970b0685e
commit 3b3f101ea6
23 changed files with 419 additions and 73 deletions

View file

@ -10,7 +10,8 @@ angular.module('quay').directive('repoPanelInfo', function () {
restrict: 'C',
scope: {
'repository': '=repository',
'builds': '=builds'
'builds': '=builds',
'isEnabled': '=isEnabled'
},
controller: function($scope, $element, ApiService, Config) {
$scope.$watch('repository', function(repository) {

View file

@ -157,10 +157,10 @@ angular.module('quay').directive('repoPanelTags', function () {
};
ApiService.getRepoImageVulnerabilities(null, params).then(function(resp) {
imageData.security_indexed = resp.security_indexed;
imageData.loading = false;
imageData.status = resp['status'];
if (imageData.security_indexed) {
if (imageData.status == 'scanned') {
var vulnerabilities = resp.data.Vulnerabilities;
imageData.hasVulnerabilities = !!vulnerabilities.length;

View file

@ -11,7 +11,8 @@ angular.module('quay').directive('createExternalNotificationDialog', function ()
scope: {
'repository': '=repository',
'counter': '=counter',
'notificationCreated': '&notificationCreated'
'notificationCreated': '&notificationCreated',
'defaultData': '=defaultData'
},
controller: function($scope, $element, ExternalNotificationData, ApiService, $timeout, StringBuilderService) {
$scope.currentEvent = null;
@ -98,6 +99,13 @@ angular.module('quay').directive('createExternalNotificationDialog', function ()
ApiService.createRepoNotification(data, params).then(function(resp) {
$scope.status = '';
$scope.notificationCreated({'notification': resp});
// Used by repository-events-summary.
if (!$scope.repository._notificationCounter) {
$scope.repository._notificationCounter = 0;
}
$scope.repository._notificationCounter++;
$('#createNotificationModal').modal('hide');
});
};
@ -154,6 +162,13 @@ angular.module('quay').directive('createExternalNotificationDialog', function ()
$scope.currentEvent = null;
$scope.currentMethod = null;
$scope.unauthorizedEmail = false;
$timeout(function() {
if ($scope.defaultData && $scope.defaultData['currentEvent']) {
$scope.setEvent($scope.defaultData['currentEvent']);
}
}, 100);
$('#createNotificationModal').modal({});
}
});

View file

@ -0,0 +1,77 @@
/**
* An element which displays a summary of events on a repository of a particular type.
*/
angular.module('quay').directive('repositoryEventsSummary', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/repository-events-summary.html',
replace: false,
transclude: false,
restrict: 'C',
scope: {
'repository': '=repository',
'isEnabled': '=isEnabled',
'eventFilter': '@eventFilter',
'hasEvents': '=hasEvents'
},
controller: function($scope, ApiService, ExternalNotificationData) {
var loadNotifications = function() {
if (!$scope.repository || !$scope.isEnabled || !$scope.eventFilter || $scope.notificationsResource) {
return;
}
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name
};
$scope.notificationsResource = ApiService.listRepoNotificationsAsResource(params).get(
function(resp) {
var notifications = [];
resp.notifications.forEach(function(notification) {
if (notification.event == $scope.eventFilter) {
notifications.push(notification);
}
});
$scope.notifications = notifications;
$scope.hasEvents = !!notifications.length;
return $scope.notifications;
});
};
$scope.$watch('repository', loadNotifications);
$scope.$watch('isEnabled', loadNotifications);
$scope.$watch('eventFilter', loadNotifications);
// Watch _notificationCounter, which is set by create-external-notification-dialog. We use this
// to invalidate and reload.
$scope.$watch('repository._notificationCounter', function() {
$scope.notificationsResource = null;
loadNotifications();
});
loadNotifications();
$scope.findEnumValue = function(values, index) {
var found = null;
Object.keys(values).forEach(function(key) {
if (values[key]['index'] == index) {
found = values[key];
return
}
});
return found
};
$scope.getEventInfo = function(notification) {
return ExternalNotificationData.getEventInfo(notification.event);
};
$scope.getMethodInfo = function(notification) {
return ExternalNotificationData.getMethodInfo(notification.method);
};
}
};
return directiveDefinitionObject;
});

View file

@ -13,11 +13,29 @@ angular.module('quay').directive('repositoryEventsTable', function () {
'repository': '=repository',
'isEnabled': '=isEnabled'
},
controller: function($scope, $element, ApiService, Restangular, UtilService, ExternalNotificationData) {
controller: function($scope, $element, $timeout, ApiService, Restangular, UtilService, ExternalNotificationData, $location) {
$scope.showNewNotificationCounter = 0;
$scope.newNotificationData = {};
var loadNotifications = function() {
if (!$scope.repository || $scope.notificationsResource || !$scope.isEnabled) { return; }
if (!$scope.repository || !$scope.isEnabled) { return; }
var add_event = $location.search()['add_event'];
if (add_event) {
$timeout(function() {
$scope.newNotificationData = {
'currentEvent': ExternalNotificationData.getEventInfo(add_event)
};
$scope.askCreateNotification();
}, 100);
$location.search('add_event', null);
}
if ($scope.notificationsResource) {
return;
}
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name
@ -73,6 +91,13 @@ angular.module('quay').directive('repositoryEventsTable', function () {
var index = $.inArray(notification, $scope.notifications);
if (index < 0) { return; }
$scope.notifications.splice(index, 1);
if (!$scope.repository._notificationCounter) {
$scope.repository._notificationCounter = 0;
}
$scope.repository._notificationCounter++;
}, ApiService.errorDisplay('Cannot delete notification'));
};

View file

@ -55,26 +55,33 @@
$scope.packagesResource = ApiService.getRepoImagePackagesAsResource(params).get(function(packages) {
$scope.packages = packages;
return packages;
});
};
$scope.loadImageVulnerabilities = function() {
if (!Features.SECURITY_SCANNER || $scope.vulnerabilitiesResource) { return; }
$scope.VulnerabilityLevels = VulnerabilityService.getLevels();
var params = {
'repository': namespace + '/' + name,
'imageid': imageid
};
$scope.vulnerabilitiesResource = ApiService.getRepoImageVulnerabilitiesAsResource(params).get(function(resp) {
$scope.vulerabilityInfo = resp;
$scope.vulnerabilityInfo = resp;
$scope.vulnerabilities = [];
resp.data.Vulnerabilities.forEach(function(vuln) {
vuln_copy = jQuery.extend({}, vuln);
vuln_copy['index'] = VulnerabilityService.LEVELS[vuln['Priority']]['index'];
$scope.vulnerabilities.push(vuln_copy);
});
if (resp.data && resp.data.Vulnerabilities) {
resp.data.Vulnerabilities.forEach(function(vuln) {
vuln_copy = jQuery.extend({}, vuln);
vuln_copy['index'] = VulnerabilityService.LEVELS[vuln['Priority']]['index'];
$scope.vulnerabilities.push(vuln_copy);
});
}
return resp;
});
};

View file

@ -17,6 +17,7 @@
var imageLoader = ImageLoaderService.getLoader($scope.namespace, $scope.name);
// Tab-enabled counters.
$scope.infoShown = 0;
$scope.tagsShown = 0;
$scope.logsShown = 0;
$scope.buildsShown = 0;
@ -119,6 +120,10 @@
$scope.viewScope.selectedTags = $.unique(tagNames.split(','));
};
$scope.showInfo = function() {
$scope.infoShown++;
};
$scope.showBuilds = function() {
$scope.buildsShown++;
};

View file

@ -47,12 +47,12 @@ function(Config, Features, VulnerabilityService) {
events.push({
'id': 'vulnerability_found',
'title': 'Package Vulnerability Found',
'icon': 'fa-flag',
'icon': 'fa-bug',
'fields': [
{
'name': 'level',
'type': 'enum',
'title': 'Minimum Severity Level',
'title': 'Minimum Priority Level',
'values': VulnerabilityService.LEVELS,
}
]