Skip to content

Commit 373e131

Browse files
petervomartinpitt
authored andcommitted
kubernetes: Use RBAC apiGroup when policybinds is not available
When there are no policybindings use the RBAC apiGroup to watch and update rolebindings. These are different objects from the openshift rolebindings but the openshift ones are still not watchable. I'm not entirely happy with this, but we need to be able to work with both types for now. Long term we need to either drop compatibility faster or change the way we load schema to make it more dynamic. Closes cockpit-project#8038
1 parent 806d266 commit 373e131

File tree

4 files changed

+95
-49
lines changed

4 files changed

+95
-49
lines changed

pkg/kubernetes/scripts/policy.js

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,51 @@
3232
'$rootScope',
3333
'kubeLoader',
3434
'kubeMethods',
35-
function($q, $rootScope, loader, methods) {
35+
'kubeSelect',
36+
'KubeWatch',
37+
'KubeRequest',
38+
'KUBE_SCHEMA',
39+
function($q, $rootScope, loader, methods, select, watch, KubeRequest, KUBE_SCHEMA) {
40+
41+
var apiGroup;
42+
var RBAC_GROUP = "rbac.authorization.k8s.io";
43+
var RBAC_API = "/apis/rbac.authorization.k8s.io/v1beta1";
44+
var POLICY_BINDING_API = KUBE_SCHEMA["RoleBinding"]["api"];
45+
var watchPromise;
46+
47+
function setupRoleBinding(group) {
48+
KUBE_SCHEMA["RoleBinding"]["api"] = group ? RBAC_API : POLICY_BINDING_API;
49+
KUBE_SCHEMA["rolebindings"]["api"] = group ? RBAC_API : POLICY_BINDING_API;
50+
apiGroup = group;
51+
expireSAR(null);
52+
expireWhoCan(null);
53+
return group ? "rolebindings" : "policybindings";
54+
}
55+
56+
function ensureWatchType() {
57+
if (!watchPromise) {
58+
watchPromise = new KubeRequest("GET", "/oapi/v1")
59+
.then(function(response) {
60+
var data = response.data || {};
61+
var i, l = data.resources || [];
62+
for(i = 0; i < l.length; i++ ) {
63+
if (l[i].kind == "PolicyBinding")
64+
return setupRoleBinding();
65+
}
66+
return setupRoleBinding(RBAC_GROUP);
67+
}, function(err) {
68+
console.warn("Error getting API", err);
69+
return setupRoleBinding();
70+
});
71+
}
72+
return watchPromise;
73+
}
3674

3775
/*
3876
* Data loading hacks:
3977
*
40-
* We would like to watch rolebindings, but sadly that's not supported
41-
* by origin. So we have to watch policybindings and then infer the
78+
* We would like to watch rolebindings, but not all versions support
79+
* that. So we have to watch policybindings and then infer the
4280
* rolebindings from there.
4381
*
4482
* In addition we would like to be able to load User and Group objects,
@@ -47,6 +85,15 @@
4785
*/
4886
loader.listen(function(present, removed) {
4987
var link, expire = { };
88+
89+
/* If reseting clear status */
90+
if (!present && !removed) {
91+
expireSAR(null);
92+
expireWhoCan(null);
93+
watchPromise = null;
94+
return;
95+
}
96+
5097
for (link in removed) {
5198
if (removed[link].kind == "PolicyBinding") {
5299
update_rolebindings(removed[link].roleBindings, true);
@@ -82,6 +129,10 @@
82129
if (link in loader.objects)
83130
return;
84131

132+
/* Don't show system groups */
133+
if (subject.kind == "Group" && subject.name.indexOf("system:") === 0)
134+
return;
135+
85136
/* An interim object, until perhaps the real thing can be loaded */
86137
var interim = { kind: subject.kind, apiVersion: "v1", metadata: { name: subject.name } };
87138
if (subject.namespace)
@@ -236,7 +287,6 @@
236287
var name = toName(role);
237288
var binding = {
238289
kind: "RoleBinding",
239-
apiVersion: "v1",
240290
metadata: {
241291
name: name,
242292
namespace: namespace,
@@ -246,7 +296,8 @@
246296
groupNames: [],
247297
subjects: [],
248298
roleRef: {
249-
name: role
299+
name: role,
300+
kind: "ClusterRole",
250301
}
251302
};
252303
addToArray(roleArray(binding, "subjects"), subjects);
@@ -255,6 +306,7 @@
255306
}
256307

257308
function removeFromRole(project, role, subject) {
309+
subject.apiGroup = apiGroup;
258310
var namespace = toName(project);
259311
return modifyRole(namespace, role, function(data) {
260312
removeFromArray(roleArray(data, "subjects"), subject);
@@ -268,26 +320,26 @@
268320
});
269321
}
270322

271-
function removeMemberFromPolicyBinding(policyBinding, project, subjectRoleBindings, subject) {
323+
function removeMemberFromProject(project, subjectRoleBindings, subject) {
272324
var registryRoles = ["registry-admin", "registry-editor", "registry-viewer"];
273325
var chain = $q.when();
274-
var defaultPolicybinding;
275326
var roleBindings = [];
327+
var defaultPolicybinding = select().kind("PolicyBinding")
328+
.namespace(project)
329+
.name(":default").one();
330+
subject.apiGroup = apiGroup;
276331

277-
if(policyBinding && policyBinding.one()){
278-
defaultPolicybinding = policyBinding.one();
332+
if(defaultPolicybinding)
279333
roleBindings = defaultPolicybinding.roleBindings;
280-
}
281-
angular.forEach(subjectRoleBindings, function(o) {
282-
angular.forEach(roleBindings, function(role) {
283-
//Since we only added registry roles
284-
//remove ONLY registry roles
285-
if (( indexOf(registryRoles, role.name) !== -1) && role.name === o.metadata.name) {
286-
chain = chain.then(function() {
287-
return removeFromRole(project, role.name, subject);
288-
});
289-
}
290-
});
334+
335+
angular.forEach(subjectRoleBindings, function(role) {
336+
//Since we only added registry roles
337+
//remove ONLY registry roles
338+
if (indexOf(registryRoles, role.roleRef.name) !== -1) {
339+
chain = chain.then(function() {
340+
return removeFromRole(project, role.roleRef.name, subject);
341+
});
342+
}
291343
});
292344
return chain;
293345
}
@@ -334,14 +386,18 @@
334386

335387
return {
336388
watch: function watch(until) {
337-
loader.watch("policybindings", until).then(function() {
338-
expireWhoCan(null);
389+
ensureWatchType().then(function (what) {
390+
loader.watch(what, until)
391+
.then(function() {
392+
expireWhoCan(null);
393+
});
339394
});
340395
},
341396
whoCan: function whoCan(project, verb, resource) {
342397
return lookupWhoCan(toName(project), verb, resource);
343398
},
344399
addToRole: function addToRole(project, role, subject) {
400+
subject.apiGroup = apiGroup;
345401
var namespace = toName(project);
346402
return modifyRole(namespace, role, function(data) {
347403
addToArray(roleArray(data, "subjects"), subject);
@@ -356,8 +412,8 @@
356412
});
357413
},
358414
removeFromRole: removeFromRole,
359-
removeMemberFromPolicyBinding: removeMemberFromPolicyBinding,
360-
subjectAccessReview: subjectAccessReview,
415+
removeMemberFromProject: removeMemberFromProject,
416+
subjectAccessReview: subjectAccessReview
361417
};
362418
}
363419
]);

pkg/kubernetes/scripts/projects.js

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,11 @@
106106
'projectActions',
107107
'ListingState',
108108
'roleActions',
109-
function($scope, $routeParams, $location, select, loader, projectData, projectAction, ListingState, roleAction) {
109+
'projectPolicy',
110+
function($scope, $routeParams, $location, select, loader, projectData, projectAction, ListingState, roleAction, projectPolicy) {
110111
loader.watch("users", $scope);
111112
loader.watch("groups", $scope);
112-
loader.watch("policybindings", $scope);
113+
projectPolicy.watch($scope);
113114
var namespace = $routeParams["namespace"] || "";
114115
$scope.projName = namespace;
115116
if (namespace) {
@@ -149,9 +150,11 @@
149150
'projectActions',
150151
'roleActions',
151152
'ListingState',
152-
function($scope, $routeParams, $location, select, loader, projectData, projectAction, roleActions, ListingState) {
153+
'projectPolicy',
154+
function($scope, $routeParams, $location, select, loader, projectData, projectAction, roleActions, ListingState, projectPolicy) {
153155
loader.watch("users", $scope);
154156
loader.watch("groups", $scope);
157+
projectPolicy.watch($scope);
155158
var user = $routeParams["user"] || "";
156159
$scope.userName = user;
157160
if (user) {
@@ -192,17 +195,14 @@
192195
'projectActions',
193196
'roleActions',
194197
'ListingState',
195-
function($scope, $routeParams, $location, select, loader, projectData, projectAction, roleActions, ListingState) {
198+
'projectPolicy',
199+
function($scope, $routeParams, $location, select, loader, projectData, projectAction, roleActions, ListingState, projectPolicy) {
196200
loader.watch("users", $scope);
197201
loader.watch("groups", $scope);
202+
projectPolicy.watch($scope);
198203
var group = $routeParams["group"] || "";
199204
$scope.groupName = group;
200205
if (group) {
201-
var groupObj = select().kind("Group").name(group);
202-
if (!groupObj || groupObj.length < 1) {
203-
$scope.group = null;
204-
return;
205-
}
206206
$scope.group = function() {
207207
return select().kind("Group").name(group).one();
208208
};
@@ -1154,9 +1154,6 @@
11541154
'memberActions',
11551155
"fields",
11561156
function($q, $scope, kselect, loader, methods, projectData, projectPolicy, $location, memberActions, fields) {
1157-
function getPolicyBinding(namespace){
1158-
return kselect().kind("PolicyBinding").namespace(namespace).name(":default");
1159-
}
11601157
function getMembers() {
11611158
var members = [];
11621159
var groups = getGroups();
@@ -1228,7 +1225,6 @@
12281225
};
12291226
function removeMemberFromParents(member) {
12301227
var chain = $q.when();
1231-
var policyBinding;
12321228
var groups = projectData.getGroupsWithMember(getGroups(), member.metadata.name);
12331229
angular.forEach(groups, function(g) {
12341230
chain = chain.then(function() {
@@ -1237,14 +1233,13 @@
12371233
});
12381234
var projects = projectData.getProjectsWithMember(getProjects(), member.metadata.name);
12391235
angular.forEach(projects, function(project) {
1240-
policyBinding = getPolicyBinding(project.metadata.name);
12411236
var subjectRoleBindings = projectData.subjectRoleBindings(member.metadata.name, project.metadata.name);
12421237
var subject = {
12431238
kind: member.kind,
12441239
name: member.metadata.name,
12451240
};
12461241
chain = chain.then(function() {
1247-
return projectPolicy.removeMemberFromPolicyBinding(policyBinding,
1242+
return projectPolicy.removeMemberFromProject(
12481243
project.metadata.name, subjectRoleBindings, subject);
12491244
});
12501245
});
@@ -1277,13 +1272,12 @@
12771272
//Project
12781273
var member = $scope.fields.memberObj;
12791274
var project = $scope.fields.parentObj.metadata.name;
1280-
var policyBinding = getPolicyBinding(project);
12811275
var subjectRoleBindings = projectData.subjectRoleBindings(member.metadata.name, project);
12821276
var subject = {
12831277
kind: member.kind,
12841278
name: member.metadata.name,
12851279
};
1286-
return projectPolicy.removeMemberFromPolicyBinding(policyBinding, project, subjectRoleBindings, subject);
1280+
return projectPolicy.removeMemberFromProject(project, subjectRoleBindings, subject);
12871281
}
12881282
};
12891283

@@ -1307,9 +1301,6 @@
13071301
function getProjects() {
13081302
return kselect().kind("Project");
13091303
}
1310-
function getPolicyBinding(namespace){
1311-
return kselect().kind("PolicyBinding").namespace(namespace).name(":default");
1312-
}
13131304
$scope.select = {
13141305
member: 'Select Member',
13151306
members: getUsers(),
@@ -1319,17 +1310,15 @@
13191310
$scope.fields.grpProjects = projectData.getProjectsWithMember(getProjects(), fields.group.metadata.name);
13201311
function removeMemberFromParents(member) {
13211312
var chain = $q.when();
1322-
var policyBinding;
13231313
var projects = projectData.getProjectsWithMember(getProjects(), member.metadata.name);
13241314
angular.forEach(projects, function(project) {
1325-
policyBinding = getPolicyBinding(project.metadata.name);
13261315
var subjectRoleBindings = projectData.subjectRoleBindings(member.metadata.name, project.metadata.name);
13271316
var subject = {
13281317
kind: member.kind,
13291318
name: member.metadata.name,
13301319
};
13311320
chain = chain.then(function() {
1332-
return projectPolicy.removeMemberFromPolicyBinding(policyBinding,
1321+
return projectPolicy.removeMemberFromProject(
13331322
project.metadata.name, subjectRoleBindings, subject);
13341323
});
13351324
});

pkg/kubernetes/scripts/registry.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@
105105
'filterService',
106106
function($scope, loader, select, discoverSettings, imageData, imageActions, projectActions, projectData, projectPolicy, filter) {
107107
loader.load("projects");
108-
/* Watch the policybindings for project access changes */
109-
loader.watch("policybindings", $scope);
108+
/* Watch the for project access changes */
109+
projectPolicy.watch($scope);
110110

111111
/*
112112
* For now the dashboard has to watch all images in

test/verify/check-openshift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,7 @@ class TestRegistry(MachineCase):
600600

601601
self.login_and_go("/kubernetes/registry")
602602
b.wait_present(".dashboard-images")
603+
b.wait_present("#content a[href='#/images/marmalade']")
603604
b.click("#content a[href='#/images/marmalade']")
604605
b.wait_present("a i.pficon-add-circle-o")
605606

0 commit comments

Comments
 (0)