1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-05 22:05:23 +02:00

feat(registries): add registry management (#930)

This commit is contained in:
Anthony Lapenna 2017-06-20 13:00:32 +02:00 committed by GitHub
parent 9360f24d89
commit 08c5a5a4f6
75 changed files with 2317 additions and 621 deletions

View file

@ -0,0 +1,8 @@
angular.module('portainer').component('porAccessManagement', {
templateUrl: 'app/directives/accessManagement/porAccessManagement.html',
controller: 'porAccessManagementController',
bindings: {
accessControlledEntity: '<',
updateAccess: '&'
}
});

View file

@ -0,0 +1,134 @@
<div class="row">
<div class="col-sm-6">
<rd-widget>
<rd-widget-header classes="col-sm-12 col-md-6 nopadding" icon="fa-users" title="Users and teams">
<div class="pull-md-right pull-lg-right">
Items per page:
<select ng-model="$ctrl.state.pagination_count_accesses" ng-change="$ctrl.changePaginationCountAccesses()">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</div>
</rd-widget-header>
<rd-widget-taskbar classes="col-sm-12 nopadding">
<div class="col-sm-12 col-md-6 nopadding">
<button class="btn btn-primary btn-sm" ng-click="$ctrl.authorizeAllAccesses()" ng-disabled="$ctrl.accesses.length === 0 || $ctrl.filteredUsers.length === 0"><i class="fa fa-user-plus space-right" aria-hidden="true"></i>Authorize all</button>
</div>
<div class="col-sm-12 col-md-6 nopadding">
<input type="text" id="filter" ng-model="$ctrl.state.filterUsers" placeholder="Filter..." class="form-control input-sm" />
</div>
</rd-widget-taskbar>
<rd-widget-body classes="no-padding">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>
<a ng-click="$ctrl.orderAccesses('Name')">
Name
<span ng-show="$ctrl.state.sortAccessesBy == 'Name' && !$ctrl.state.sortAccessesReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="$ctrl.state.sortAccessesBy == 'Name' && $ctrl.state.sortAccessesReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a ng-click="$ctrl.orderAccesses('Type')">
Type
<span ng-show="$ctrl.state.sortAccessesBy == 'Type' && !$ctrl.state.sortAccessesReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="$ctrl.state.sortAccessesBy == 'Type' && $ctrl.state.sortAccessesReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
</tr>
</thead>
<tbody>
<tr ng-click="$ctrl.authorizeAccess(user)" class="interactive" dir-paginate="user in $ctrl.accesses | filter:$ctrl.state.filterUsers | orderBy:$ctrl.state.sortAccessesBy:$ctrl.state.sortAccessesReverse | itemsPerPage: $ctrl.state.pagination_count_accesses">
<td>{{ user.Name }}</td>
<td>
<i class="fa" ng-class="user.Type === 'user' ? 'fa-user' : 'fa-users'" aria-hidden="true" style="margin-right: 2px;"></i>
{{ user.Type }}
</td>
</tr>
<tr ng-if="!$ctrl.accesses">
<td colspan="2" class="text-center text-muted">Loading...</td>
</tr>
<tr ng-if="$ctrl.accesses.length === 0 || ($ctrl.accesses | filter:$ctrl.state.filterUsers | orderBy:$ctrl.state.sortAccessesBy:$ctrl.state.sortAccessesReverse | itemsPerPage: $ctrl.state.pagination_count_accesses).length === 0">
<td colspan="2" class="text-center text-muted">No user or team available.</td>
</tr>
</tbody>
</table>
<div ng-if="$ctrl.accesses" class="pull-left pagination-controls">
<dir-pagination-controls></dir-pagination-controls>
</div>
</div>
</rd-widget-body>
</rd-widget>
</div>
<div class="col-sm-6">
<rd-widget>
<rd-widget-header classes="col-sm-12 col-md-6 nopadding" icon="fa-users" title="Authorized users and teams">
<div class="pull-md-right pull-lg-right">
Items per page:
<select ng-model="$ctrl.state.pagination_count_authorizedAccesses" ng-change="$ctrl.changePaginationCountAuthorizedAccesses()">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</div>
</rd-widget-header>
<rd-widget-taskbar classes="col-sm-12 nopadding">
<div class="col-sm-12 col-md-6 nopadding">
<button class="btn btn-primary btn-sm" ng-click="$ctrl.unauthorizeAllAccesses()" ng-disabled="$ctrl.authorizedAccesses.length === 0 || $ctrl.filteredAuthorizedUsers.length === 0"><i class="fa fa-user-times space-right" aria-hidden="true"></i>Deny all</button>
</div>
<div class="col-sm-12 col-md-6 nopadding">
<input type="text" id="filter" ng-model="$ctrl.state.filterAuthorizedUsers" placeholder="Filter..." class="form-control input-sm" />
</div>
</rd-widget-taskbar>
<rd-widget-body classes="no-padding">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>
<a ng-click="$ctrl.orderAuthorizedAccesses('Name')">
Name
<span ng-show="$ctrl.state.sortAuthorizedAccessesBy == 'Name' && !$ctrl.state.sortAuthorizedAccessesReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="$ctrl.state.sortAuthorizedAccessesBy == 'Name' && $ctrl.state.sortAuthorizedAccessesReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a ng-click="$ctrl.orderAuthorizedAccesses('Type')">
Type
<span ng-show="$ctrl.state.sortAuthorizedAccessesBy == 'Type' && !$ctrl.state.sortAuthorizedAccessesReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="$ctrl.state.sortAuthorizedAccessesBy == 'Type' && $ctrl.state.sortAuthorizedAccessesReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
</tr>
</thead>
<tbody>
<tr ng-click="$ctrl.unauthorizeAccess(user)" class="interactive" pagination-id="table_authaccess" dir-paginate="user in $ctrl.authorizedAccesses | filter:$ctrl.state.filterAuthorizedUsers | orderBy:$ctrl.state.sortAuthorizedAccessesBy:$ctrl.state.sortAuthorizedAccessesReverse | itemsPerPage: $ctrl.state.pagination_count_authorizedAccesses">
<td>{{ user.Name }}</td>
<td>
<i class="fa" ng-class="user.Type === 'user' ? 'fa-user' : 'fa-users'" aria-hidden="true" style="margin-right: 2px;"></i>
{{ user.Type }}
</td>
</tr>
<tr ng-if="!$ctrl.authorizedAccesses">
<td colspan="2" class="text-center text-muted">Loading...</td>
</tr>
<tr ng-if="$ctrl.authorizedAccesses.length === 0 || (authorizedAccesses | filter:state.filterAuthorizedUsers | orderBy:sortTypeAuthorizedAccesses:sortReverseAuthorizedAccesses | itemsPerPage: state.pagination_count_authorizedAccesses).length === 0">
<td colspan="2" class="text-center text-muted">No authorized user or team.</td>
</tr>
</tbody>
</table>
<div ng-if="$ctrl.authorizedAccesses" class="pull-left pagination-controls">
<dir-pagination-controls pagination-id="table_authaccess"></dir-pagination-controls>
</div>
</div>
</rd-widget-body>
</rd-widget>
</div>
</div>

View file

@ -0,0 +1,157 @@
angular.module('portainer')
.controller('porAccessManagementController', ['AccessService', 'Pagination', 'Notifications',
function (AccessService, Pagination, Notifications) {
var ctrl = this;
ctrl.state = {
pagination_count_accesses: Pagination.getPaginationCount('access_management_accesses'),
pagination_count_authorizedAccesses: Pagination.getPaginationCount('access_management_AuthorizedAccesses'),
sortAccessesBy: 'Type',
sortAccessesReverse: false,
sortAuthorizedAccessesBy: 'Type',
sortAuthorizedAccessesReverse: false
};
ctrl.orderAccesses = function(sortBy) {
ctrl.state.sortAccessesReverse = (ctrl.state.sortAccessesBy === sortBy) ? !ctrl.state.sortAccessesReverse : false;
ctrl.state.sortAccessesBy = sortBy;
};
ctrl.orderAuthorizedAccesses = function(sortBy) {
ctrl.state.sortAuthorizedAccessesReverse = (ctrl.state.sortAuthorizedAccessesBy === sortBy) ? !ctrl.state.sortAuthorizedAccessesReverse : false;
ctrl.state.sortAuthorizedAccessesBy = sortBy;
};
ctrl.changePaginationCountAuthorizedAccesses = function() {
Pagination.setPaginationCount('access_management_AuthorizedAccesses', ctrl.state.pagination_count_authorizedAccesses);
};
ctrl.changePaginationCountAccesses = function() {
Pagination.setPaginationCount('access_management_accesses', ctrl.state.pagination_count_accesses);
};
function dispatchUserAndTeamIDs(accesses, users, teams) {
angular.forEach(accesses, function (access) {
if (access.Type === 'user') {
users.push(access.Id);
} else if (access.Type === 'team') {
teams.push(access.Id);
}
});
}
function processAuthorizedIDs(accesses, authorizedAccesses) {
var authorizedUserIDs = [];
var authorizedTeamIDs = [];
if (accesses) {
dispatchUserAndTeamIDs(accesses, authorizedUserIDs, authorizedTeamIDs);
}
if (authorizedAccesses) {
dispatchUserAndTeamIDs(authorizedAccesses, authorizedUserIDs, authorizedTeamIDs);
}
return {
userIDs: authorizedUserIDs,
teamIDs: authorizedTeamIDs
};
}
function removeFromAccesses(access, accesses) {
_.remove(accesses, function(n) {
return n.Id === access.Id;
});
}
function removeFromAccessIDs(accessId, accessIDs) {
_.remove(accessIDs, function(n) {
return n === accessId;
});
}
ctrl.authorizeAccess = function(access) {
var accessData = processAuthorizedIDs(null, ctrl.authorizedAccesses);
var authorizedUserIDs = accessData.userIDs;
var authorizedTeamIDs = accessData.teamIDs;
if (access.Type === 'user') {
authorizedUserIDs.push(access.Id);
} else if (access.Type === 'team') {
authorizedTeamIDs.push(access.Id);
}
ctrl.updateAccess({ userAccesses: authorizedUserIDs, teamAccesses: authorizedTeamIDs })
.then(function success(data) {
removeFromAccesses(access, ctrl.accesses);
ctrl.authorizedAccesses.push(access);
Notifications.success('Accesses successfully updated');
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to update accesses');
});
};
ctrl.unauthorizeAccess = function(access) {
var accessData = processAuthorizedIDs(null, ctrl.authorizedAccesses);
var authorizedUserIDs = accessData.userIDs;
var authorizedTeamIDs = accessData.teamIDs;
if (access.Type === 'user') {
removeFromAccessIDs(access.Id, authorizedUserIDs);
} else if (access.Type === 'team') {
removeFromAccessIDs(access.Id, authorizedTeamIDs);
}
ctrl.updateAccess({ userAccesses: authorizedUserIDs, teamAccesses: authorizedTeamIDs })
.then(function success(data) {
removeFromAccesses(access, ctrl.authorizedAccesses);
ctrl.accesses.push(access);
Notifications.success('Accesses successfully updated');
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to update accesses');
});
};
ctrl.unauthorizeAllAccesses = function() {
ctrl.updateAccess({ userAccesses: [], teamAccesses: [] })
.then(function success(data) {
ctrl.accesses = ctrl.accesses.concat(ctrl.authorizedAccesses);
ctrl.authorizedAccesses = [];
Notifications.success('Accesses successfully updated');
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to update accesses');
});
};
ctrl.authorizeAllAccesses = function() {
var accessData = processAuthorizedIDs(ctrl.accesses, ctrl.authorizedAccesses);
var authorizedUserIDs = accessData.userIDs;
var authorizedTeamIDs = accessData.teamIDs;
ctrl.updateAccess({ userAccesses: authorizedUserIDs, teamAccesses: authorizedTeamIDs })
.then(function success(data) {
ctrl.authorizedAccesses = ctrl.authorizedAccesses.concat(ctrl.accesses);
ctrl.accesses = [];
Notifications.success('Accesses successfully updated');
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to update accesses');
});
};
function initComponent() {
var entity = ctrl.accessControlledEntity;
AccessService.accesses(entity.AuthorizedUsers, entity.AuthorizedTeams)
.then(function success(data) {
ctrl.accesses = data.accesses;
ctrl.authorizedAccesses = data.authorizedAccesses;
})
.catch(function error(err) {
ctrl.accesses = [];
ctrl.authorizedAccesses = [];
Notifications.error('Failure', err, 'Unable to retrieve accesses');
});
}
initComponent();
}]);

View file

@ -0,0 +1,8 @@
angular.module('portainer').component('porImageRegistry', {
templateUrl: 'app/directives/imageRegistry/porImageRegistry.html',
controller: 'porImageRegistryController',
bindings: {
'image': '=',
'registry': '='
}
});

View file

@ -0,0 +1,12 @@
<div>
<label for="image_name" class="col-sm-1 control-label text-left">Name</label>
<div class="col-sm-11 col-md-6">
<input type="text" class="form-control" ng-model="$ctrl.image" id="image_name" placeholder="e.g. myImage:myTag">
</div>
<label for="image_registry" class="col-sm-2 col-md-1 margin-sm-top control-label text-left">
Registry
</label>
<div class="col-sm-10 col-md-4 margin-sm-top">
<select ng-options="registry as registry.Name for registry in $ctrl.availableRegistries" ng-model="$ctrl.registry" id="image_registry" class="form-control"></select>
</div>
</div>

View file

@ -0,0 +1,23 @@
angular.module('portainer')
.controller('porImageRegistryController', ['$q', 'RegistryService', 'DockerHubService', 'Notifications',
function ($q, RegistryService, DockerHubService, Notifications) {
var ctrl = this;
function initComponent() {
$q.all({
registries: RegistryService.registries(),
dockerhub: DockerHubService.dockerhub()
})
.then(function success(data) {
var dockerhub = data.dockerhub;
var registries = data.registries;
ctrl.availableRegistries = [dockerhub].concat(registries);
ctrl.registry = dockerhub;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve registries');
});
}
initComponent();
}]);