1412 lines
62 KiB
JavaScript
1412 lines
62 KiB
JavaScript
/*
|
|
* This file is part of Cockpit.
|
|
*
|
|
* Copyright (C) 2015 Red Hat, Inc.
|
|
*
|
|
* Cockpit is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Cockpit is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
(function() {
|
|
var angular = require('angular');
|
|
require('angular-route');
|
|
require('angular-dialog.js');
|
|
|
|
require('./kube-client');
|
|
require('./listing');
|
|
require('./policy');
|
|
|
|
require('../views/projects-page.html');
|
|
require('../views/project-page.html');
|
|
require('../views/user-page.html');
|
|
require('../views/group-page.html');
|
|
require('../views/project-panel.html');
|
|
require('../views/project-body.html');
|
|
require('../views/user-body.html');
|
|
require('../views/group-panel.html');
|
|
require('../views/user-panel.html');
|
|
require('../views/project-listing.html');
|
|
require('../views/project-modify.html');
|
|
require('../views/project-modify.html');
|
|
require('../views/project-delete.html');
|
|
require('../views/add-group-dialog.html');
|
|
require('../views/user-group-add.html');
|
|
require('../views/user-group-remove.html');
|
|
require('../views/group-delete.html');
|
|
require('../views/add-user-dialog.html');
|
|
require('../views/user-modify.html');
|
|
require('../views/user-add-membership.html');
|
|
require('../views/user-remove-membership.html');
|
|
require('../views/user-delete.html');
|
|
require('../views/add-member-role-dialog.html');
|
|
require('../views/remove-role-dialog.html');
|
|
require('../views/add-role-dialog.html');
|
|
|
|
function toName(object) {
|
|
if (object && typeof object == "object")
|
|
return object.metadata.name;
|
|
else
|
|
return object;
|
|
}
|
|
|
|
angular.module('registry.projects', [
|
|
'ngRoute',
|
|
'ui.cockpit',
|
|
'kubeClient',
|
|
'kubernetes.listing',
|
|
'registry.policy',
|
|
])
|
|
|
|
.config(['$routeProvider',
|
|
function($routeProvider) {
|
|
$routeProvider
|
|
.when('/projects/:namespace?', {
|
|
controller: 'ProjectsCtrl',
|
|
templateUrl: function(params) {
|
|
if (!params['namespace'])
|
|
return 'views/projects-page.html';
|
|
else
|
|
return 'views/project-page.html';
|
|
}
|
|
})
|
|
.when('/users/:user', {
|
|
controller: 'UserCtrl',
|
|
templateUrl: function(params) {
|
|
return 'views/user-page.html';
|
|
}
|
|
})
|
|
.when('/groups/:group', {
|
|
controller: 'GroupCtrl',
|
|
templateUrl: function(params) {
|
|
return 'views/group-page.html';
|
|
}
|
|
});
|
|
}
|
|
])
|
|
|
|
.controller('ProjectsCtrl', [
|
|
'$scope',
|
|
'$routeParams',
|
|
'$location',
|
|
'kubeSelect',
|
|
'kubeLoader',
|
|
'projectData',
|
|
'projectActions',
|
|
'ListingState',
|
|
'roleActions',
|
|
'projectPolicy',
|
|
function($scope, $routeParams, $location, select, loader, projectData, projectAction, ListingState, roleAction, projectPolicy) {
|
|
loader.watch("users", $scope);
|
|
loader.watch("groups", $scope);
|
|
projectPolicy.watch($scope);
|
|
var namespace = $routeParams["namespace"] || "";
|
|
$scope.projName = namespace;
|
|
if (namespace) {
|
|
$scope.listing = new ListingState($scope);
|
|
$scope.project = function() {
|
|
return select().kind("Project")
|
|
.name(namespace)
|
|
.one();
|
|
};
|
|
} else {
|
|
$scope.listing = new ListingState($scope);
|
|
$scope.projects = function() {
|
|
return select().kind("Project");
|
|
};
|
|
}
|
|
$scope.$on("activate", function(ev, id) {
|
|
ev.preventDefault();
|
|
$location.path(id);
|
|
});
|
|
angular.extend($scope, projectData);
|
|
angular.extend($scope, roleAction);
|
|
angular.extend($scope, projectAction);
|
|
$scope.users = function() {
|
|
return select().kind("User");
|
|
};
|
|
$scope.groups = function() {
|
|
return select().kind("Group");
|
|
};
|
|
}
|
|
])
|
|
|
|
.controller('UserCtrl', [
|
|
'$scope',
|
|
'$routeParams',
|
|
'$location',
|
|
'kubeSelect',
|
|
'kubeLoader',
|
|
'projectData',
|
|
'projectActions',
|
|
'roleActions',
|
|
'ListingState',
|
|
'projectPolicy',
|
|
function($scope, $routeParams, $location, select, loader, projectData, projectAction, roleActions, ListingState, projectPolicy) {
|
|
loader.watch("users", $scope);
|
|
loader.watch("groups", $scope);
|
|
projectPolicy.watch($scope);
|
|
var user = $routeParams["user"] || "";
|
|
$scope.userName = user;
|
|
if (user) {
|
|
$scope.user = function() {
|
|
return select().kind("User")
|
|
.name(user)
|
|
.one();
|
|
};
|
|
$scope.listing = new ListingState($scope);
|
|
$scope.$on("activate", function(ev, id) {
|
|
ev.preventDefault();
|
|
$location.path(id);
|
|
});
|
|
} else {
|
|
$scope.listing = new ListingState($scope);
|
|
$location.path("/projects");
|
|
}
|
|
$scope.projects = function() {
|
|
return select().kind("Project");
|
|
};
|
|
$scope.groups = function() {
|
|
return select().kind("Group");
|
|
};
|
|
$scope.users = function() {
|
|
return select().kind("User");
|
|
};
|
|
angular.extend($scope, projectData);
|
|
angular.extend($scope, projectAction);
|
|
angular.extend($scope, roleActions);
|
|
}
|
|
])
|
|
|
|
.controller('GroupCtrl', [
|
|
'$scope',
|
|
'$routeParams',
|
|
'$location',
|
|
'kubeSelect',
|
|
'kubeLoader',
|
|
'projectData',
|
|
'projectActions',
|
|
'roleActions',
|
|
'ListingState',
|
|
'projectPolicy',
|
|
function($scope, $routeParams, $location, select, loader, projectData, projectAction, roleActions, ListingState, projectPolicy) {
|
|
loader.watch("users", $scope);
|
|
loader.watch("groups", $scope);
|
|
projectPolicy.watch($scope);
|
|
var group = $routeParams["group"] || "";
|
|
$scope.groupName = group;
|
|
if (group) {
|
|
$scope.group = function() {
|
|
return select().kind("Group")
|
|
.name(group)
|
|
.one();
|
|
};
|
|
$scope.listing = new ListingState($scope);
|
|
$scope.$on("activate", function(ev, id) {
|
|
ev.preventDefault();
|
|
$location.path(id);
|
|
});
|
|
} else {
|
|
$scope.listing = new ListingState($scope);
|
|
$location.path("/projects");
|
|
}
|
|
$scope.projects = function() {
|
|
return select().kind("Project");
|
|
};
|
|
$scope.groups = function() {
|
|
return select().kind("Group");
|
|
};
|
|
$scope.users = function() {
|
|
return select().kind("User");
|
|
};
|
|
angular.extend($scope, projectData);
|
|
angular.extend($scope, projectAction);
|
|
angular.extend($scope, roleActions);
|
|
}
|
|
])
|
|
|
|
.factory("projectData", [
|
|
'$q',
|
|
'kubeSelect',
|
|
'kubeLoader',
|
|
'projectPolicy',
|
|
function($q, select, loader, policy) {
|
|
var registryRoles = [{ ocRole: "registry-admin", displayRole :"Admin" },
|
|
{ ocRole:"registry-editor", displayRole :"Push" },
|
|
{ ocRole:"registry-viewer", displayRole :"Pull" }];
|
|
|
|
function getRegistryRolesMap() {
|
|
return registryRoles;
|
|
}
|
|
function getDisplayRole(ocRole) {
|
|
var i;
|
|
var displayRole;
|
|
for (i = registryRoles.length - 1; i >= 0; i--) {
|
|
if (registryRoles[i].ocRole === ocRole) {
|
|
displayRole = registryRoles[i].displayRole;
|
|
break;
|
|
}
|
|
}
|
|
return displayRole;
|
|
}
|
|
function getOcRolesList() {
|
|
var ocRoles = [];
|
|
angular.forEach(registryRoles, function(r) {
|
|
ocRoles.push(r.ocRole);
|
|
});
|
|
return ocRoles;
|
|
}
|
|
function getAllRoles(member, project) {
|
|
if (!member && !project)
|
|
return [];
|
|
var projectName = toName(project);
|
|
var roleBinds = subjectRoleBindings(member, projectName);
|
|
var meta;
|
|
var ret = [];
|
|
angular.forEach(roleBinds, function(roleBind) {
|
|
meta = roleBind.metadata || { };
|
|
if (meta.name)
|
|
ret.push(meta.name);
|
|
});
|
|
return ret;
|
|
}
|
|
function getRegistryRoles(member, project) {
|
|
if (!member && !project)
|
|
return [];
|
|
var projectName = toName(project);
|
|
var roleBinds = subjectRoleBindings(member, projectName);
|
|
var ocRegistryRoles = getOcRolesList();
|
|
var roles = [];
|
|
var meta;
|
|
angular.forEach(roleBinds, function(roleBind) {
|
|
meta = roleBind.metadata || { };
|
|
if (meta.name && ocRegistryRoles.indexOf(meta.name) !== -1) {
|
|
if (roles.indexOf(getDisplayRole(meta.name) === -1))
|
|
roles.push(getDisplayRole(meta.name));
|
|
}
|
|
});
|
|
return roles;
|
|
}
|
|
function isRegistryRole(member, displayRole, project) {
|
|
var oc_roles = getRegistryRoles(member, project);
|
|
if (oc_roles.indexOf(displayRole) !== -1) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
function hasRegistryRole(member, project) {
|
|
var oc_roles = getRegistryRoles(member, project);
|
|
if (oc_roles.length === 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
function isRoles(member, namespace) {
|
|
var oc_roles = getAllRoles(member, namespace);
|
|
if (oc_roles.length === 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
function getProjectsWithMember(projects, member) {
|
|
if (!projects && !member)
|
|
return [];
|
|
var projList = [];
|
|
if (member) {
|
|
angular.forEach(projects, function(project) {
|
|
if (project && subjectIsMember(member, project.metadata.name))
|
|
projList.push(project);
|
|
});
|
|
}
|
|
return projList;
|
|
}
|
|
|
|
function getGroupsWithMember(groups, member) {
|
|
if (!groups && !member)
|
|
return [];
|
|
var grpList = [];
|
|
if (member) {
|
|
angular.forEach(groups, function(group) {
|
|
if (group && group.users && group.users.indexOf(member) != -1)
|
|
grpList.push(group);
|
|
});
|
|
}
|
|
return grpList;
|
|
}
|
|
function getMembershipOfUser(projects, groups, user) {
|
|
var members = [];
|
|
var userProjects = getProjectsWithMember(projects, user);
|
|
angular.forEach(userProjects, function(project) {
|
|
members.push(project.metadata.name);
|
|
});
|
|
var userGroups = getGroupsWithMember(groups, user);
|
|
angular.forEach(userGroups, function(group) {
|
|
members.push(group.metadata.name);
|
|
});
|
|
return members;
|
|
}
|
|
/*
|
|
* To use this you would have a user or group, and do:
|
|
*
|
|
* rolebindings = select().kind("RoleBindings").containsSubject(user_name);
|
|
* rolebindings = select().kind("RoleBindings").containsSubject(user_object);
|
|
* rolebindings = select().kind("RoleBindings").containsSubject(group_object);
|
|
*/
|
|
select.register({
|
|
name: "containsSubject",
|
|
digests: function(arg) {
|
|
var meta, i, len, subjects;
|
|
var ret = [];
|
|
if (typeof arg == "string") {
|
|
ret.push(arg);
|
|
} else if (arg.kind == "User" || arg.kind == "Group") {
|
|
meta = arg.metadata || { };
|
|
ret.push(meta.name + ":" + arg.kind);
|
|
} else if (arg.kind == "RoleBinding") {
|
|
subjects = arg.subjects || [];
|
|
for (i = 0, len = subjects.length; i < len; i++) {
|
|
ret.push(subjects[i].name);
|
|
ret.push(subjects[i].name + ":" + subjects[i].kind);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
});
|
|
|
|
function subjectRoleBindings(subject, namespace) {
|
|
return select().kind("RoleBinding")
|
|
.namespace(namespace)
|
|
.containsSubject(subject);
|
|
}
|
|
|
|
function subjectIsMember(subject, namespace) {
|
|
if (!subject || !namespace)
|
|
return null;
|
|
namespace = toName(namespace);
|
|
return !!subjectRoleBindings(subject, namespace).one();
|
|
}
|
|
|
|
function formatMembers(members, kind) {
|
|
var mlist = "";
|
|
var i;
|
|
if (!members || members.length === 0)
|
|
return mlist;
|
|
if (members.length <= 3) {
|
|
for (i = members.length - 1; i >= 0; i--) {
|
|
if (i > 0)
|
|
mlist += ",";
|
|
mlist += members[i];
|
|
}
|
|
} else {
|
|
if (kind === "Groups") {
|
|
mlist = members.length + " " + kind;
|
|
} else if (kind === "Users") {
|
|
mlist = members.length + " " + kind;
|
|
}
|
|
}
|
|
return mlist;
|
|
}
|
|
|
|
var sharedVerb = "get";
|
|
var sharedResource = "imagestreams/layers";
|
|
var sharedRole = "registry-viewer";
|
|
var sharedKind = "Group";
|
|
var anonymousGroup = "system:unauthenticated";
|
|
var sharedGroup = "system:authenticated";
|
|
var registryAdmin = "registry-admin";
|
|
|
|
function shareImages(project, accessPolicy) {
|
|
var anonSubject = {
|
|
kind: sharedKind,
|
|
name: anonymousGroup,
|
|
};
|
|
var sharedSubject = {
|
|
kind: sharedKind,
|
|
name: sharedGroup,
|
|
};
|
|
if (accessPolicy === "anonymous") {
|
|
return policy.addToRole(project, sharedRole, anonSubject)
|
|
.then(function() {
|
|
return policy.addToRole(project, sharedRole, sharedSubject);
|
|
});
|
|
} else if (accessPolicy === "shared") {
|
|
return policy.removeFromRole(project, sharedRole, anonSubject)
|
|
.then(function() {
|
|
return policy.addToRole(project, sharedRole, sharedSubject);
|
|
});
|
|
} else {
|
|
return policy.removeFromRole(project, sharedRole, anonSubject)
|
|
.then(function() {
|
|
return policy.removeFromRole(project, sharedRole, sharedSubject);
|
|
});
|
|
}
|
|
}
|
|
|
|
function sharedImages(project) {
|
|
if (!project)
|
|
return null;
|
|
|
|
var response = policy.whoCan(project, sharedVerb, sharedResource);
|
|
if (!response)
|
|
return null;
|
|
|
|
var projState = {};
|
|
projState[anonymousGroup] = null;
|
|
projState[sharedGroup] = null;
|
|
|
|
var i, len;
|
|
var groups = response.groups || [];
|
|
for (i = 0, len = groups.length; i < len; i++) {
|
|
if (projState.hasOwnProperty(groups[i]))
|
|
projState[groups[i]] = true;
|
|
}
|
|
|
|
if (projState[anonymousGroup])
|
|
return "anonymous";
|
|
else if (projState[sharedGroup])
|
|
return "shared";
|
|
return "private";
|
|
}
|
|
|
|
function makeRegistryAdmin(project, currentUser) {
|
|
var subject = {
|
|
kind: 'User',
|
|
name: toName(currentUser),
|
|
};
|
|
// if system:admin then return
|
|
if (currentUser && currentUser.groups &&
|
|
currentUser.groups.indexOf("system:cluster-admins") !== -1) {
|
|
return $q.when({ });
|
|
}
|
|
var oc_roles = getRegistryRoles(currentUser, project);
|
|
if (oc_roles.indexOf(registryAdmin) !== -1) {
|
|
// for templates with registryAdmin
|
|
// if registryAdmin role exists then return
|
|
return $q.when({ });
|
|
}
|
|
return policy.addToRole(project, registryAdmin, subject);
|
|
}
|
|
|
|
function isSameUser(currUser, userName) {
|
|
return currUser ? toName(currUser) === toName(userName) : false;
|
|
}
|
|
|
|
return {
|
|
subjectRoleBindings: subjectRoleBindings,
|
|
subjectIsMember: subjectIsMember,
|
|
formatMembers: formatMembers,
|
|
shareImages: shareImages,
|
|
sharedImages: sharedImages,
|
|
getAllRoles: getAllRoles,
|
|
isRegistryRole: isRegistryRole,
|
|
isRoles: isRoles,
|
|
getRegistryRolesMap: getRegistryRolesMap,
|
|
getRegistryRoles: getRegistryRoles,
|
|
getGroupsWithMember: getGroupsWithMember,
|
|
getProjectsWithMember: getProjectsWithMember,
|
|
getMembershipOfUser: getMembershipOfUser,
|
|
isSameUser: isSameUser,
|
|
makeRegistryAdmin: makeRegistryAdmin,
|
|
hasRegistryRole: hasRegistryRole,
|
|
};
|
|
}
|
|
])
|
|
|
|
.directive('projectPanel', [
|
|
'kubeLoader',
|
|
'kubeSelect',
|
|
'projectData',
|
|
'roleActions',
|
|
function(loader, select, projectData, roleActions) {
|
|
return {
|
|
restrict: 'A',
|
|
scope: true,
|
|
link: function(scope, element, attrs) {
|
|
var tab = 'main';
|
|
scope.tab = function(name, ev) {
|
|
if (ev) {
|
|
tab = name;
|
|
ev.stopPropagation();
|
|
}
|
|
return tab === name;
|
|
};
|
|
var currProject = scope.id;
|
|
angular.extend(scope, projectData);
|
|
angular.extend(scope, roleActions);
|
|
loader.load("Project", null, currProject);
|
|
scope.project = function() {
|
|
return select().kind("Project")
|
|
.name(currProject)
|
|
.one();
|
|
};
|
|
},
|
|
templateUrl: "views/project-panel.html"
|
|
};
|
|
}
|
|
])
|
|
|
|
.directive('projectBody', [
|
|
'projectData',
|
|
function(data) {
|
|
return {
|
|
restrict: 'A',
|
|
templateUrl: 'views/project-body.html',
|
|
link: function(scope, element, attrs) {
|
|
scope.sharedImages = data.sharedImages;
|
|
},
|
|
};
|
|
}
|
|
])
|
|
|
|
.directive('userBody', [
|
|
function() {
|
|
return {
|
|
restrict: 'A',
|
|
templateUrl: 'views/user-body.html',
|
|
link: function(scope, element, attrs) {
|
|
},
|
|
};
|
|
}
|
|
])
|
|
|
|
.directive('groupPanel', [
|
|
'kubeLoader',
|
|
'kubeSelect',
|
|
'projectData',
|
|
function(loader, select, projectData) {
|
|
return {
|
|
restrict: 'A',
|
|
scope: true,
|
|
link: function(scope, element, attrs) {
|
|
var tab = 'main';
|
|
scope.tab = function(name, ev) {
|
|
if (ev) {
|
|
tab = name;
|
|
ev.stopPropagation();
|
|
}
|
|
return tab === name;
|
|
};
|
|
|
|
var currGroup = scope.id;
|
|
loader.load("Groups");
|
|
scope.group = function() {
|
|
return select().kind("Group")
|
|
.name(currGroup)
|
|
.one();
|
|
};
|
|
scope.projects = function() {
|
|
return select().kind("Project");
|
|
};
|
|
angular.extend(scope, projectData);
|
|
},
|
|
templateUrl: "views/group-panel.html"
|
|
};
|
|
}
|
|
])
|
|
|
|
.directive('userPanel', [
|
|
'kubeLoader',
|
|
'kubeSelect',
|
|
'projectData',
|
|
'projectActions',
|
|
function(loader, select, projectData, projectAction) {
|
|
return {
|
|
restrict: 'A',
|
|
scope: true,
|
|
link: function(scope, element, attrs) {
|
|
var tab = 'main';
|
|
scope.tab = function(name, ev) {
|
|
if (ev) {
|
|
tab = name;
|
|
ev.stopPropagation();
|
|
}
|
|
return tab === name;
|
|
};
|
|
|
|
var currUser = scope.id;
|
|
loader.load("Users");
|
|
angular.extend(scope, projectData);
|
|
angular.extend(scope, projectAction);
|
|
scope.user = function() {
|
|
return select().kind("User")
|
|
.name(currUser)
|
|
.one();
|
|
};
|
|
scope.groups = function() {
|
|
return select().kind("Group");
|
|
};
|
|
scope.projects = function() {
|
|
return select().kind("Project");
|
|
};
|
|
},
|
|
templateUrl: "views/user-panel.html"
|
|
};
|
|
}
|
|
])
|
|
|
|
.directive('projectListing',
|
|
function() {
|
|
return {
|
|
restrict: 'A',
|
|
templateUrl: 'views/project-listing.html'
|
|
};
|
|
}
|
|
)
|
|
|
|
.factory('projectActions', [
|
|
'$modal',
|
|
'projectData',
|
|
function($modal, projectData) {
|
|
function createProject(currentUser) {
|
|
return $modal.open({
|
|
controller: 'ProjectModifyCtrl',
|
|
templateUrl: 'views/project-modify.html',
|
|
resolve: {
|
|
dialogData: function() {
|
|
return { currUser: currentUser };
|
|
}
|
|
},
|
|
}).result;
|
|
}
|
|
|
|
function modifyProject(project) {
|
|
return $modal.open({
|
|
animation: false,
|
|
controller: 'ProjectModifyCtrl',
|
|
templateUrl: 'views/project-modify.html',
|
|
resolve: {
|
|
dialogData: function() {
|
|
return { project: project };
|
|
}
|
|
},
|
|
}).result;
|
|
}
|
|
function removeProject(project) {
|
|
return $modal.open({
|
|
animation: false,
|
|
controller: 'ProjectModifyCtrl',
|
|
templateUrl: 'views/project-delete.html',
|
|
resolve: {
|
|
dialogData: function() {
|
|
return { project: project };
|
|
}
|
|
},
|
|
}).result;
|
|
}
|
|
function createGroup() {
|
|
return $modal.open({
|
|
controller: 'GroupNewCtrl',
|
|
templateUrl: 'views/add-group-dialog.html',
|
|
});
|
|
}
|
|
function addUserToGroup(groupObj) {
|
|
return $modal.open({
|
|
controller: 'GroupChangeCtrl',
|
|
templateUrl: 'views/user-group-add.html',
|
|
resolve:{
|
|
fields: function() {
|
|
return { group: groupObj };
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function removeUserFromGroup(user, groupObj) {
|
|
return $modal.open({
|
|
controller: 'GroupChangeCtrl',
|
|
templateUrl: 'views/user-group-remove.html',
|
|
resolve:{
|
|
fields: function() {
|
|
return { group: groupObj, user: user };
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function removeGroup(projects, groupObj) {
|
|
return $modal.open({
|
|
controller: 'GroupChangeCtrl',
|
|
templateUrl: 'views/group-delete.html',
|
|
resolve:{
|
|
fields: function() {
|
|
var members = projectData.getProjectsWithMember(projects, groupObj.metadata.name);
|
|
return { group: groupObj, projects: projects, members: members };
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function createUser() {
|
|
return $modal.open({
|
|
controller: 'UserNewCtrl',
|
|
templateUrl: 'views/add-user-dialog.html',
|
|
});
|
|
}
|
|
function modifyUser(userObj) {
|
|
return $modal.open({
|
|
animation: false,
|
|
controller: 'UserChangeCtrl',
|
|
templateUrl: 'views/user-modify.html',
|
|
resolve: {
|
|
fields: function() {
|
|
return { user: userObj };
|
|
}
|
|
},
|
|
}).result;
|
|
}
|
|
function addMemberToParent(memberObj) {
|
|
return $modal.open({
|
|
controller: 'UserChangeCtrl',
|
|
templateUrl: 'views/user-add-membership.html',
|
|
resolve:{
|
|
fields: function() {
|
|
return { memberObj: memberObj };
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function removeMemberFromParent(memberObj, parentObj) {
|
|
return $modal.open({
|
|
controller: 'UserChangeCtrl',
|
|
templateUrl: 'views/user-remove-membership.html',
|
|
resolve:{
|
|
fields: function() {
|
|
return { parentObj: parentObj, memberObj: memberObj };
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function removeUser(projects, groups, userObj) {
|
|
return $modal.open({
|
|
controller: 'UserChangeCtrl',
|
|
templateUrl: 'views/user-delete.html',
|
|
resolve:{
|
|
fields: function() {
|
|
var members = projectData.getMembershipOfUser(projects, groups, userObj.metadata.name);
|
|
return { user: userObj, projects: projects, groups: groups,
|
|
members: members };
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return {
|
|
createProject: createProject,
|
|
modifyProject: modifyProject,
|
|
createGroup: createGroup,
|
|
createUser: createUser,
|
|
removeProject: removeProject,
|
|
removeUser: removeUser,
|
|
modifyUser: modifyUser,
|
|
addMemberToParent: addMemberToParent,
|
|
removeMemberFromParent: removeMemberFromParent,
|
|
removeGroup: removeGroup,
|
|
addUserToGroup: addUserToGroup,
|
|
removeUserFromGroup: removeUserFromGroup,
|
|
};
|
|
}
|
|
])
|
|
|
|
.factory('roleActions', [
|
|
'$modal',
|
|
function($modal) {
|
|
function addMember(project) {
|
|
return $modal.open({
|
|
controller: 'MemberNewCtrl',
|
|
templateUrl: 'views/add-member-role-dialog.html',
|
|
resolve: {
|
|
fields: function() {
|
|
return { namespace: toName(project) };
|
|
}
|
|
},
|
|
});
|
|
}
|
|
function changeRole(member, roleMp, roles, project) {
|
|
return $modal.open({
|
|
controller: 'ChangeRoleCtrl',
|
|
templateUrl: function() {
|
|
if (roles.indexOf(roleMp.displayRole) >= 0) {
|
|
return 'views/remove-role-dialog.html';
|
|
} else {
|
|
return 'views/add-role-dialog.html';
|
|
}
|
|
},
|
|
resolve: {
|
|
fields: function() {
|
|
return { member: member, ocRole: roleMp.ocRole,
|
|
displayRole: roleMp.displayRole, roles: roles,
|
|
namespace: toName(project) };
|
|
}
|
|
},
|
|
});
|
|
}
|
|
return {
|
|
addMember: addMember,
|
|
changeRole: changeRole,
|
|
};
|
|
}
|
|
])
|
|
|
|
.controller('ChangeRoleCtrl', [
|
|
'$q',
|
|
'$scope',
|
|
'projectPolicy',
|
|
'kubeLoader',
|
|
'kubeSelect',
|
|
'fields',
|
|
function($q, $scope, projectPolicy, loader, kselect, fields) {
|
|
$scope.fields = fields;
|
|
var namespace = $scope.fields.namespace;
|
|
|
|
$scope.performCreate = function performCreate() {
|
|
var role = $scope.fields.ocRole;
|
|
var memberObj = $scope.fields.member;
|
|
var subject = {
|
|
kind: memberObj.kind,
|
|
name: memberObj.metadata.name,
|
|
};
|
|
return projectPolicy.addToRole(namespace, role, subject);
|
|
};
|
|
|
|
$scope.performRemove = function performRemove() {
|
|
var role = $scope.fields.ocRole;
|
|
var memberObj = $scope.fields.member;
|
|
var subject = {
|
|
kind: memberObj.kind,
|
|
name: memberObj.metadata.name,
|
|
};
|
|
return projectPolicy.removeFromRole(namespace, role, subject);
|
|
};
|
|
}
|
|
])
|
|
|
|
.controller('MemberNewCtrl', [
|
|
'$q',
|
|
'$scope',
|
|
'projectData',
|
|
'projectPolicy',
|
|
'kubeLoader',
|
|
'kubeSelect',
|
|
'fields',
|
|
'gettextCatalog',
|
|
function($q, $scope, projectData, projectPolicy, loader, kselect, fields, gettextCatalog) {
|
|
const _ = gettextCatalog.getString.bind(gettextCatalog);
|
|
var selectMember = _("Select Member");
|
|
var NAME_RE = /^[a-z0-9_.]([-a-z0-9@._:]*[a-z0-9._:])?$/;
|
|
var selectRole = _("Select Role");
|
|
|
|
loader.watch("User", $scope);
|
|
loader.watch("Group", $scope);
|
|
|
|
$scope.selected = {
|
|
member: selectMember,
|
|
getMembers: getAllMembers,
|
|
displayRole: selectRole,
|
|
roles: projectData.getRegistryRolesMap(),
|
|
kind: "",
|
|
ocRole: "",
|
|
};
|
|
$scope.itemTracker = function(item) {
|
|
return item.kind + "/" + item.metadata.name;
|
|
};
|
|
var namespace = fields.namespace;
|
|
|
|
/*
|
|
* This function is called during Angular rendering. Make sure we
|
|
* use the same array over and over again, so angular doesn't
|
|
* try to digest again.
|
|
*/
|
|
function getAllMembers() {
|
|
var users = kselect().kind("User");
|
|
var groups = kselect().kind("Group");
|
|
var members = [];
|
|
angular.forEach(users, function(user) {
|
|
members.push(user);
|
|
});
|
|
angular.forEach(groups, function(group) {
|
|
members.push(group);
|
|
});
|
|
return members;
|
|
}
|
|
function validate(memberName, role) {
|
|
var defer = $q.defer();
|
|
var ex;
|
|
if (memberName !== undefined) {
|
|
if (!memberName)
|
|
ex = new Error("The member name cannot be empty.");
|
|
else if (memberName === selectMember)
|
|
ex = new Error("Please select a valid Member.");
|
|
else if (!NAME_RE.test(memberName))
|
|
ex = new Error("The member name contains invalid characters. Only letters, numbers, spaces and the following symbols are allowed: , = @ . _");
|
|
|
|
if (ex) {
|
|
ex.target = "#add_member_group";
|
|
defer.reject(ex);
|
|
}
|
|
}
|
|
if (!role || role === selectRole) {
|
|
ex = new Error("Please select a valid Role.");
|
|
ex.target = "#add_role";
|
|
defer.reject(ex);
|
|
}
|
|
|
|
if (!ex) {
|
|
defer.resolve();
|
|
}
|
|
|
|
return defer.promise;
|
|
}
|
|
$scope.performCreate = function performCreate() {
|
|
var role = $scope.selected.ocRole;
|
|
var memberName = $scope.selected.memberName;
|
|
var member = $scope.selected.member;
|
|
var memberObj, kind;
|
|
if (memberName && memberName === member) {
|
|
// dropdown value selected
|
|
memberObj = $scope.selected.memberObj;
|
|
memberName = memberObj.metadata.name;
|
|
kind = memberObj.kind;
|
|
} else if (memberName && member === selectMember) {
|
|
// input field has value
|
|
kind = "User";
|
|
} else if (!memberName && member === selectMember) {
|
|
// nothing selected
|
|
memberName = selectMember;
|
|
kind = null;
|
|
}
|
|
|
|
return validate(memberName, role).then(function() {
|
|
var subject = {
|
|
kind: kind,
|
|
name: memberName,
|
|
};
|
|
return projectPolicy.addToRole(namespace, role, subject);
|
|
});
|
|
};
|
|
}
|
|
])
|
|
|
|
.controller('ProjectModifyCtrl', [
|
|
'$q',
|
|
'$scope',
|
|
'kubeSelect',
|
|
"dialogData",
|
|
"projectData",
|
|
'$location',
|
|
'$timeout',
|
|
"kubeMethods",
|
|
"gettextCatalog",
|
|
function($q, $scope, kselect, dialogData, projectData, $location, $timeout, methods, gettextCatalog) {
|
|
var project = dialogData.project || { };
|
|
var meta = project.metadata || { };
|
|
var annotations = meta.annotations || { };
|
|
const _ = gettextCatalog.getString.bind(gettextCatalog);
|
|
|
|
var DISPLAY = "openshift.io/display-name";
|
|
var DESCRIPTION = "openshift.io/description";
|
|
|
|
var fields = {
|
|
name: meta.name || "",
|
|
display: annotations[DISPLAY] || "",
|
|
description: annotations[DESCRIPTION] || "",
|
|
access: null,
|
|
};
|
|
|
|
/*
|
|
* This data may not be available yet, so continue to ask until it is.
|
|
* The called function takes care of not making duplicate requests.
|
|
*/
|
|
var timr;
|
|
function updateShared() {
|
|
timr = null;
|
|
if (fields.access === null)
|
|
fields.access = projectData.sharedImages(meta.name);
|
|
if (fields.access === null)
|
|
timr = $timeout(updateShared, 500);
|
|
}
|
|
updateShared();
|
|
$scope.$on("$destroy", function() {
|
|
if (timr !== null)
|
|
$timeout.cancel(timr);
|
|
});
|
|
|
|
angular.extend($scope, projectData);
|
|
$scope.fields = fields;
|
|
$scope.labels = {
|
|
access: {
|
|
"private": _("Private: Allow only specific users or groups to pull images"),
|
|
"shared": _("Shared: Allow any authenticated user to pull images"),
|
|
"anonymous" : _("Anonymous: Allow all unauthenticated users to pull images"),
|
|
}
|
|
};
|
|
|
|
$scope.performDelete = function performDelete(project) {
|
|
var promise = methods.delete(project)
|
|
.then(function() {
|
|
$location.path("/projects");
|
|
}, function(ex) {
|
|
return $q.reject(ex);
|
|
});
|
|
|
|
return promise;
|
|
};
|
|
$scope.performCreate = function performCreate() {
|
|
var name = fields.name.trim();
|
|
var request = {
|
|
kind: "ProjectRequest",
|
|
apiVersion:"v1",
|
|
metadata:{ name: name, },
|
|
displayName: fields.display.trim(),
|
|
description: fields.description.trim()
|
|
};
|
|
|
|
return methods.check(request, { "metadata.name": "#project-new-name" })
|
|
.then(function() {
|
|
return methods.create(request);
|
|
})
|
|
.then(function() {
|
|
return projectData.shareImages(name, fields.access);
|
|
})
|
|
.then(function() {
|
|
return projectData.makeRegistryAdmin(name, dialogData.currUser);
|
|
});
|
|
};
|
|
|
|
$scope.performModify = function performModify() {
|
|
var anno = { };
|
|
var data = { metadata: { annotations: anno } };
|
|
|
|
var value = fields.display.trim();
|
|
if (value !== annotations[DISPLAY])
|
|
anno[DISPLAY] = value;
|
|
value = fields.description.trim();
|
|
if (value !== annotations[DESCRIPTION])
|
|
anno[DESCRIPTION] = value;
|
|
|
|
return methods.check(data, { })
|
|
.then(function() {
|
|
return $q.all([
|
|
methods.patch(project, data),
|
|
projectData.shareImages(project, fields.access)
|
|
]);
|
|
});
|
|
};
|
|
|
|
angular.extend($scope, dialogData);
|
|
}
|
|
])
|
|
|
|
.controller('UserNewCtrl', [
|
|
'$q',
|
|
'$scope',
|
|
"kubeMethods",
|
|
function($q, $scope, methods) {
|
|
var fields = {
|
|
name: "",
|
|
identities: ""
|
|
};
|
|
|
|
$scope.fields = fields;
|
|
|
|
$scope.performCreate = function performCreate() {
|
|
var identities = [];
|
|
if (fields.identities.trim() !== "")
|
|
identities = [fields.identities.trim()];
|
|
|
|
var user = {
|
|
"kind": "User",
|
|
"apiVersion": "v1",
|
|
"metadata": {
|
|
"name": fields.name.trim()
|
|
},
|
|
"identities": identities
|
|
};
|
|
|
|
return methods.check(user, { "metadata.name": "#user_name" })
|
|
.then(function() {
|
|
return methods.create(user);
|
|
});
|
|
};
|
|
}
|
|
])
|
|
|
|
.factory('memberActions', [
|
|
"kubeMethods",
|
|
function(methods) {
|
|
function removeUserFromGroup(user, group) {
|
|
var userName = toName(user);
|
|
var users = group.users || [];
|
|
var index = users.indexOf(userName);
|
|
if (index >= 0)
|
|
users.splice(index, 1);
|
|
var patchData = { "users": users };
|
|
return methods.patch(group, patchData);
|
|
}
|
|
function addUserToGroup(user, group) {
|
|
var userName = toName(user);
|
|
var users = group.users || [];
|
|
var index = users.indexOf(userName);
|
|
if (index == -1)
|
|
users.push(userName);
|
|
var patchData = { "users": users };
|
|
return methods.patch(group, patchData);
|
|
}
|
|
return {
|
|
removeUserFromGroup: removeUserFromGroup,
|
|
addUserToGroup: addUserToGroup,
|
|
};
|
|
}
|
|
])
|
|
|
|
.controller('UserChangeCtrl', [
|
|
'$q',
|
|
'$scope',
|
|
'kubeSelect',
|
|
'kubeLoader',
|
|
"kubeMethods",
|
|
'projectData',
|
|
'projectPolicy',
|
|
'$location',
|
|
'memberActions',
|
|
"fields",
|
|
"gettextCatalog",
|
|
function($q, $scope, kselect, loader, methods, projectData, projectPolicy, $location, memberActions, fields, gettextCatalog) {
|
|
const _ = gettextCatalog.getString.bind(gettextCatalog);
|
|
loader.watch("Group", $scope);
|
|
loader.watch("Project", $scope);
|
|
|
|
function getMembers() {
|
|
var members = [];
|
|
var groups = getGroups();
|
|
var projects = getProjects();
|
|
angular.forEach(groups, function(group) {
|
|
members.push(group);
|
|
});
|
|
angular.forEach(projects, function(project) {
|
|
members.push(project);
|
|
});
|
|
return members;
|
|
}
|
|
function getProjects() {
|
|
return kselect().kind("Project");
|
|
}
|
|
function getGroups() {
|
|
return kselect().kind("Group");
|
|
}
|
|
$scope.itemTracker = function(item) {
|
|
return item.kind + "/" + item.metadata.name;
|
|
};
|
|
$scope.selected = {
|
|
member: _("Select Member"),
|
|
getMembers: getMembers,
|
|
roles: projectData.getRegistryRolesMap,
|
|
role: _("Select Role"),
|
|
};
|
|
angular.extend($scope, projectData);
|
|
$scope.fields = fields;
|
|
if (fields.user && fields.user.identities)
|
|
$scope.fields.identities = fields.user.identities.toString();
|
|
else
|
|
$scope.fields.identities = '';
|
|
|
|
$scope.performModify = function performModify() {
|
|
var identities = [];
|
|
var user = $scope.fields.user;
|
|
var data = { "identities": identities };
|
|
|
|
if (fields.identities.trim() !== "") {
|
|
var idList = fields.identities.trim().split(",");
|
|
identities.push.apply(identities, idList);
|
|
}
|
|
|
|
return methods.check(data, { })
|
|
.then(function() {
|
|
return $q.all([
|
|
methods.patch(user, data),
|
|
]);
|
|
});
|
|
};
|
|
|
|
$scope.performDelete = function performDelete(user) {
|
|
var chain = $q.when();
|
|
|
|
chain = removeMemberFromParents(user);
|
|
var promise = chain.then(function() {
|
|
$location.path("/projects");
|
|
}, function(ex) {
|
|
if (ex.code === 404) {
|
|
loader.handle(user, true, "User");
|
|
$location.path("/projects");
|
|
} else {
|
|
return $q.reject(ex);
|
|
}
|
|
});
|
|
|
|
return promise;
|
|
};
|
|
function removeMemberFromParents(member) {
|
|
var chain = $q.when();
|
|
var groups = projectData.getGroupsWithMember(getGroups(), member.metadata.name);
|
|
angular.forEach(groups, function(g) {
|
|
chain = chain.then(function() {
|
|
return memberActions.removeUserFromGroup(member, g);
|
|
});
|
|
});
|
|
var projects = projectData.getProjectsWithMember(getProjects(), member.metadata.name);
|
|
angular.forEach(projects, function(project) {
|
|
var subjectRoleBindings = projectData.subjectRoleBindings(member.metadata.name, project.metadata.name);
|
|
var subject = {
|
|
kind: member.kind,
|
|
name: member.metadata.name,
|
|
};
|
|
chain = chain.then(function() {
|
|
return projectPolicy.removeMemberFromProject(
|
|
project.metadata.name, subjectRoleBindings, subject);
|
|
});
|
|
});
|
|
chain = chain.then(function() {
|
|
return methods.delete(member);
|
|
});
|
|
return chain;
|
|
}
|
|
|
|
$scope.addMemberToParent = function addMemberToParent() {
|
|
if ($scope.selected.parentObj.kind === "Project") {
|
|
var project = $scope.selected.parentObj.metadata.name;
|
|
var memberObj = $scope.fields.memberObj;
|
|
var role = $scope.selected.ocRole;
|
|
var subject = {
|
|
kind: memberObj.kind,
|
|
name: memberObj.metadata.name,
|
|
};
|
|
return projectPolicy.addToRole(project, role, subject);
|
|
} else if ($scope.selected.parentObj.kind === "Group") {
|
|
return memberActions.addUserToGroup($scope.fields.memberObj, $scope.selected.parentObj);
|
|
}
|
|
};
|
|
|
|
$scope.removeMemberFromParent = function removeMemberFromParent() {
|
|
if ($scope.fields.parentObj.kind === "Group") {
|
|
return memberActions.removeUserFromGroup($scope.fields.memberObj, $scope.fields.parentObj);
|
|
} else {
|
|
// Project
|
|
var member = $scope.fields.memberObj;
|
|
var project = $scope.fields.parentObj.metadata.name;
|
|
var subjectRoleBindings = projectData.subjectRoleBindings(member.metadata.name, project);
|
|
var subject = {
|
|
kind: member.kind,
|
|
name: member.metadata.name,
|
|
};
|
|
return projectPolicy.removeMemberFromProject(project, subjectRoleBindings, subject);
|
|
}
|
|
};
|
|
}
|
|
])
|
|
|
|
.controller('GroupChangeCtrl', [
|
|
'$q',
|
|
'$scope',
|
|
'kubeLoader',
|
|
'kubeSelect',
|
|
"kubeMethods",
|
|
'projectData',
|
|
'memberActions',
|
|
'projectPolicy',
|
|
'$location',
|
|
"fields",
|
|
'gettextCatalog',
|
|
function($q, $scope, loader, kselect, methods, projectData, memberActions, projectPolicy, $location, fields, gettextCatalog) {
|
|
const _ = gettextCatalog.getString.bind(gettextCatalog);
|
|
loader.watch("User", $scope);
|
|
loader.watch("Project", $scope);
|
|
|
|
function getUsers() {
|
|
return kselect().kind("User");
|
|
}
|
|
function getProjects() {
|
|
return kselect().kind("Project");
|
|
}
|
|
$scope.select = {
|
|
member: _("Select Member"),
|
|
getMembers: getUsers,
|
|
};
|
|
angular.extend($scope, projectData);
|
|
$scope.fields = fields;
|
|
$scope.fields.grpProjects = projectData.getProjectsWithMember(getProjects(), fields.group.metadata.name);
|
|
function removeMemberFromParents(member) {
|
|
var chain = $q.when();
|
|
var projects = projectData.getProjectsWithMember(getProjects(), member.metadata.name);
|
|
angular.forEach(projects, function(project) {
|
|
var subjectRoleBindings = projectData.subjectRoleBindings(member.metadata.name, project.metadata.name);
|
|
var subject = {
|
|
kind: member.kind,
|
|
name: member.metadata.name,
|
|
};
|
|
chain = chain.then(function() {
|
|
return projectPolicy.removeMemberFromProject(
|
|
project.metadata.name, subjectRoleBindings, subject);
|
|
});
|
|
});
|
|
chain = chain.then(function() {
|
|
return methods.delete(member);
|
|
});
|
|
return chain;
|
|
}
|
|
$scope.performDelete = function performDelete(group) {
|
|
var chain = $q.when();
|
|
|
|
chain = removeMemberFromParents(group);
|
|
var promise = chain.then(function() {
|
|
$location.path("/projects");
|
|
}, function(ex) {
|
|
if (ex.code === 404) {
|
|
$location.path("/projects");
|
|
} else {
|
|
return $q.reject(ex);
|
|
}
|
|
});
|
|
|
|
return promise;
|
|
};
|
|
$scope.addUserToGroup = function addUserToGroup() {
|
|
return memberActions.addUserToGroup($scope.select.member, $scope.fields.group);
|
|
};
|
|
$scope.removeUserFromGroup = function removeUserFromGroup() {
|
|
return memberActions.removeUserFromGroup($scope.fields.user, $scope.fields.group);
|
|
};
|
|
}
|
|
])
|
|
|
|
.controller('GroupNewCtrl', [
|
|
'$q',
|
|
'$scope',
|
|
"kubeMethods",
|
|
function($q, $scope, methods) {
|
|
var fields = {
|
|
name: ""
|
|
};
|
|
|
|
$scope.fields = fields;
|
|
$scope.performCreate = function performCreate() {
|
|
var group = {
|
|
"kind": "Group",
|
|
"apiVersion": "v1",
|
|
"metadata": {
|
|
"name": fields.name.trim()
|
|
}
|
|
};
|
|
|
|
return methods.check(group, { "metadata.name": "#group_name" })
|
|
.then(function() {
|
|
return methods.create(group);
|
|
});
|
|
};
|
|
}
|
|
]);
|
|
}());
|