Skip to content

Refresh membership page when removing 'admin' role from current user #1113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions app/scripts/controllers/membership.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ angular
var projectName = $routeParams.project;
var humanizeKind = $filter('humanizeKind');
var annotation = $filter('annotation');
var canI = $filter('canI');

var allRoles = [];

Expand Down Expand Up @@ -69,7 +70,7 @@ angular
$scope.newBinding.newRole = null;
};

var refreshRoleBindingList = function() {
var refreshRoleBindingList = function(toUpdateOnError) {
DataService
.list('rolebindings', requestContext, null , {
errorNotification: false
Expand All @@ -81,6 +82,13 @@ angular
subjectKindsForUI: MembershipService.mapRolebindingsForUI(resp.by('metadata.name'), allRoles)
});
resetForm();
}, function() {
// if the request errors but we have an object, we can at least update in place
if(toUpdateOnError) {
$scope.roleBindings[toUpdateOnError.metadata.name] = toUpdateOnError;
$scope.subjectKindsForUI = MembershipService.mapRolebindingsForUI($scope.roleBindings, allRoles);
}
resetForm();
});
};

Expand Down Expand Up @@ -238,6 +246,7 @@ angular
angular.extend($scope, {
project: project,
subjectKinds: subjectKinds,
canUpdateRolebindings: canI('rolebindings', 'update', projectName),
confirmRemove: function(subjectName, kindName, roleName) {
var redirectToProjectList = null;
var modalScope = createModalScope(subjectName, kindName, roleName, $scope.user.metadata.name);
Expand All @@ -259,11 +268,24 @@ angular
.result.then(function() {
RoleBindingsService
.removeSubject(subjectName, roleName, $scope.roleBindings, requestContext)
.then(function() {
.then(function(updateRolebinding) {
if(redirectToProjectList) {
$location.url("./");
} else {
refreshRoleBindingList();
AuthorizationService
.getProjectRules(projectName, true)
.then(function() {
refreshRoleBindingList(updateRolebinding[0]);
// test if the current user can still edit roles.
// if not, remove permissions & exit edit mode.
var canEdit = canI('rolebindings', 'update', projectName);
angular.extend($scope, {
canUpdateRolebindings: canEdit,
mode: {
edit: $scope.mode.edit ? canEdit : false
}
});
});
showAlert('rolebindingUpdate', 'success', messages.remove.success({
roleName: roleName,
subjectName: subjectName
Expand Down
11 changes: 6 additions & 5 deletions app/scripts/services/authorization.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

angular.module("openshiftConsole")
.factory("AuthorizationService", function($q, $cacheFactory, Logger, $interval, APIService, DataService){

var currentProject = null;
var cachedRulesByProject = $cacheFactory('rulesCache', {
number: 10
Expand Down Expand Up @@ -34,7 +34,7 @@ angular.module("openshiftConsole")
// Check if resource name meets one of following conditions, since those resources can't be create/update via `Add to project` page:
// - 'projectrequests'
// - subresource that contains '/', eg: 'builds/source', 'builds/logs', ...
// - resource is in REVIEW_RESOURCES list
// - resource is in REVIEW_RESOURCES list
var checkResource = function(resource) {
if (resource === "projectrequests" || _.contains(resource, "/") || _.contains(REVIEW_RESOURCES, resource)) {
return false;
Expand All @@ -52,12 +52,13 @@ angular.module("openshiftConsole")
});
};

var getProjectRules = function(projectName) {
// forceRefresh is a boolean to bust the cache & request new perms
var getProjectRules = function(projectName, forceRefresh) {
var deferred = $q.defer();
currentProject = projectName;
var projectRules = cachedRulesByProject.get(projectName);
var rulesResource = "selfsubjectrulesreviews";
if (!projectRules || projectRules.forceRefresh) {
if (!projectRules || projectRules.forceRefresh || forceRefresh) {
// Check if APIserver contains 'selfsubjectrulesreviews' resource. If not switch to permissive mode.
if (APIService.apiInfo(rulesResource)) {
Logger.log("AuthorizationService, loading user rules for " + projectName + " project");
Expand Down Expand Up @@ -127,7 +128,7 @@ angular.module("openshiftConsole")
_canI(rules, verb, '*', '*' ) ||
_canI(rules, verb, r.group, '*' ) ||
_canI(rules, verb, '*', r.resource);
};
};

var canIAddToProject = function(projectName) {
if (permissiveMode) {
Expand Down
10 changes: 8 additions & 2 deletions app/scripts/services/membership/roleBindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,14 @@ angular
cleanBinding(binding);
binding.subjects = _.reject(binding.subjects, {name: subjectName});
return binding.subjects.length ?
DataService.update('rolebindings', binding.metadata.name, binding, context):
DataService.delete('rolebindings', binding.metadata.name, context);
DataService.update('rolebindings', binding.metadata.name, binding, context) :
DataService.delete('rolebindings', binding.metadata.name, context)
// For a delete, resp is simply a 201 or less useful object.
// Instead, this intercepts the response & returns the binding object
// with the empty .subjects[] list.
.then(function() {
return binding;
});
}));
};

Expand Down
2 changes: 1 addition & 1 deletion app/views/membership.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ <h1>
<a
class="pull-right btn btn-default"
href=""
ng-if="'rolebindings' | canI : 'update'"
ng-if="canUpdateRolebindings"
ng-click="toggleEditMode()">
<span ng-if="!(mode.edit)">Edit Membership</span>
<span ng-if="mode.edit">Done Editing</span>
Expand Down
118 changes: 66 additions & 52 deletions dist/scripts/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -1072,31 +1072,31 @@ return _.some(a.resources, function(b) {
return l(b) && !_.isEmpty(_.intersection(a.verbs, [ "*", "create", "update" ]));
});
});
}, n = function(b) {
var d = a.defer();
}, n = function(b, d) {
var j = a.defer();
g = b;
var j = h.get(b), l = "selfsubjectrulesreviews";
if (!j || j.forceRefresh) if (e.apiInfo(l)) {
var l = h.get(b), n = "selfsubjectrulesreviews";
if (!l || l.forceRefresh || d) if (e.apiInfo(n)) {
c.log("AuthorizationService, loading user rules for " + b + " project");
var n = {
var o = {
kind:"SelfSubjectRulesReview",
apiVersion:"v1"
};
f.create(l, null, n, {
f.create(n, null, o, {
namespace:b
}).then(function(a) {
var c = k(a.status.rules), e = m(a.status.rules);
var c = k(a.status.rules), d = m(a.status.rules);
h.put(b, {
rules:c,
canAddToProject:e,
canAddToProject:d,
forceRefresh:!1,
cacheTimestamp:_.now()
}), d.resolve();
}), j.resolve();
}, function() {
i = !0, d.resolve();
i = !0, j.resolve();
});
} else c.log("AuthorizationService, resource 'selfsubjectrulesreviews' is not part of APIserver. Switching into permissive mode."), i = !0, d.resolve(); else c.log("AuthorizationService, using cached rules for " + b + " project"), _.now() - j.cacheTimestamp >= 6e5 && (j.forceRefresh = !0), d.resolve();
return d.promise;
} else c.log("AuthorizationService, resource 'selfsubjectrulesreviews' is not part of APIserver. Switching into permissive mode."), i = !0, j.resolve(); else c.log("AuthorizationService, using cached rules for " + b + " project"), _.now() - l.cacheTimestamp >= 6e5 && (l.forceRefresh = !0), j.resolve();
return j.promise;
}, o = function(a) {
return _.get(h.get(a || g), [ "rules" ]);
}, p = function(a, b, c, d) {
Expand Down Expand Up @@ -2892,7 +2892,9 @@ return a.all(_.map(i, function(a) {
var d = e();
return a = _.extend(d, a), g(a), a.subjects = _.reject(a.subjects, {
name:c
}), a.subjects.length ? b.update("rolebindings", a.metadata.name, a, h) :b["delete"]("rolebindings", a.metadata.name, h);
}), a.subjects.length ? b.update("rolebindings", a.metadata.name, a, h) :b["delete"]("rolebindings", a.metadata.name, h).then(function() {
return a;
});
}));
}, k = function(a, d, e) {
return b.list("rolebindings", a, function(a) {
Expand Down Expand Up @@ -5170,7 +5172,7 @@ a !== b && (localStorage.setItem("monitoring.eventsidebar.collapsed", c.renderOp
});
}));
} ]), angular.module("openshiftConsole").controller("MembershipController", [ "$filter", "$location", "$routeParams", "$scope", "$timeout", "$uibModal", "AuthService", "AuthorizationService", "DataService", "ProjectsService", "MembershipService", "RoleBindingsService", "RolesService", function(a, b, c, d, e, f, g, h, i, j, k, l, m) {
var n, o = c.project, p = a("humanizeKind"), q = a("annotation"), r = [], s = {
var n, o = c.project, p = a("humanizeKind"), q = a("annotation"), r = a("canI"), s = [], t = {
notice:{
yourLastRole:_.template('Removing the role "<%= roleName %>" may completely remove your ability to see this project.')
},
Expand All @@ -5195,69 +5197,71 @@ exists:_.template('The role "<%= roleName %>" has already been granted to "<%= s
}
},
errorReason:_.template('Reason: "<%= httpErr %>"')
}, t = function(a, b, c, e, f) {
}, u = function(a, b, c, e, f) {
f = f || d, f.alerts[a] = {
type:b,
message:c,
details:e
};
}, u = function() {
d.disableAddForm = !1, d.newBinding.name = "", d.newBinding.namespace = o, d.newBinding.newRole = null;
}, v = function() {
d.disableAddForm = !1, d.newBinding.name = "", d.newBinding.namespace = o, d.newBinding.newRole = null;
}, w = function(a) {
i.list("rolebindings", n, null, {
errorNotification:!1
}).then(function(a) {
angular.extend(d, {
canShowRoles:!0,
roleBindings:a.by("metadata.name"),
subjectKindsForUI:k.mapRolebindingsForUI(a.by("metadata.name"), r)
}), u();
subjectKindsForUI:k.mapRolebindingsForUI(a.by("metadata.name"), s)
}), v();
}, function() {
a && (d.roleBindings[a.metadata.name] = a, d.subjectKindsForUI = k.mapRolebindingsForUI(d.roleBindings, s)), v();
});
}, w = function(b, c) {
}, x = function(b, c) {
d.disableAddForm = !0, l.create(b, c, o, n).then(function() {
v(), t("rolebindingCreate", "success", s.update.subject.success({
w(), u("rolebindingCreate", "success", t.update.subject.success({
roleName:b.metadata.name,
subjectName:c.name
}));
}, function(d) {
u(), t("rolebindingCreateFail", "error", s.update.subject.error({
v(), u("rolebindingCreateFail", "error", t.update.subject.error({
roleName:b.metadata.name,
subjectName:c.name
}), s.errorReason({
}), t.errorReason({
httpErr:a("getErrorDetails")(d)
}));
});
}, x = function(b, c, e) {
}, y = function(b, c, e) {
d.disableAddForm = !0, l.addSubject(b, c, e, n).then(function() {
v(), t("rolebindingUpdate", "success", s.update.subject.success({
w(), u("rolebindingUpdate", "success", t.update.subject.success({
roleName:b.roleRef.name,
subjectName:c.name
}));
}, function(d) {
u(), t("rolebindingUpdateFail", "error", s.update.subject.error({
v(), u("rolebindingUpdateFail", "error", t.update.subject.error({
roleName:b.roleRef.name,
subjectName:c.name
}), s.errorReason({
}), t.errorReason({
httpErr:a("getErrorDetails")(d)
}));
});
}, y = {};
c.tab && (y[c.tab] = !0);
var z = k.getSubjectKinds();
}, z = {};
c.tab && (z[c.tab] = !0);
var A = k.getSubjectKinds();
angular.extend(d, {
selectedTab:y,
selectedTab:z,
projectName:o,
alerts:{},
forms:{},
emptyMessage:"Loading...",
subjectKinds:z,
subjectKinds:A,
newBinding:{
role:"",
kind:c.tab || "User",
name:""
},
toggleEditMode:function() {
u(), d.mode.edit = !d.mode.edit;
v(), d.mode.edit = !d.mode.edit;
},
mode:{
edit:!1
Expand All @@ -5283,10 +5287,10 @@ return a ? e + (q(a, "description") || b) :b;
}
}
});
var A = function(a, b, c, e) {
var B = function(a, b, c, e) {
var f = {
alerts:{},
detailsMarkup:s.remove.areYouSure.html.subject({
detailsMarkup:t.remove.areYouSure.html.subject({
roleName:c,
kindName:p(b),
subjectName:a
Expand All @@ -5295,12 +5299,12 @@ okButtonText:"Remove",
okButtonClass:"btn-danger",
cancelButtonText:"Cancel"
};
return _.isEqual(a, e) && (f.detailsMarkup = s.remove.areYouSure.html.self({
return _.isEqual(a, e) && (f.detailsMarkup = t.remove.areYouSure.html.self({
roleName:c,
subjectName:a
}), k.isLastRole(d.user.metadata.name, d.roleBindings) && t("currentUserLastRole", "error", s.notice.yourLastRole({
}), k.isLastRole(d.user.metadata.name, d.roleBindings) && u("currentUserLastRole", "error", t.notice.yourLastRole({
roleName:c
}), null, f)), _.isEqual(b, "ServiceAccount") && _.startsWith(c, "system:") && t("editingServiceAccountRole", "error", s.warning.serviceAccount(), null, f), f;
}), null, f)), _.isEqual(b, "ServiceAccount") && _.startsWith(c, "system:") && u("editingServiceAccountRole", "error", t.warning.serviceAccount(), null, f), f;
};
g.withUser().then(function(a) {
d.user = a;
Expand All @@ -5315,31 +5319,41 @@ a && !_.includes(d.projects, a) ? d.projects = [ a ].concat(b) :d.projects = b;
}
});
}), j.get(c.project).then(_.spread(function(c, e) {
n = e, v(), angular.extend(d, {
n = e, w(), angular.extend(d, {
project:c,
subjectKinds:z,
subjectKinds:A,
canUpdateRolebindings:r("rolebindings", "update", o),
confirmRemove:function(c, e, g) {
var h = null, i = A(c, e, g, d.user.metadata.name);
_.isEqual(c, d.user.metadata.name) && k.isLastRole(d.user.metadata.name, d.roleBindings) && (h = !0), f.open({
var i = null, j = B(c, e, g, d.user.metadata.name);
_.isEqual(c, d.user.metadata.name) && k.isLastRole(d.user.metadata.name, d.roleBindings) && (i = !0), f.open({
animation:!0,
templateUrl:"views/modals/confirm.html",
controller:"ConfirmModalController",
resolve:{
modalConfig:function() {
return i;
return j;
}
}
}).result.then(function() {
l.removeSubject(c, g, d.roleBindings, n).then(function() {
h ? b.url("./") :(v(), t("rolebindingUpdate", "success", s.remove.success({
l.removeSubject(c, g, d.roleBindings, n).then(function(a) {
i ? b.url("./") :(h.getProjectRules(o, !0).then(function() {
w(a[0]);
var b = r("rolebindings", "update", o);
angular.extend(d, {
canUpdateRolebindings:b,
mode:{
edit:!!d.mode.edit && b
}
});
}), u("rolebindingUpdate", "success", t.remove.success({
roleName:g,
subjectName:c
})));
}, function(b) {
t("rolebindingUpdateFail", "error", s.remove.error({
u("rolebindingUpdateFail", "error", t.remove.error({
roleName:g,
subjectName:c
}), s.errorReason({
}), t.errorReason({
httpErr:a("getErrorDetails")(b)
}));
});
Expand All @@ -5357,23 +5371,23 @@ name:c.metadata.name
});
g && _.some(g.subjects, {
name:a
}) ? t("rolebindingUpdate", "info", s.update.subject.exists({
}) ? u("rolebindingUpdate", "info", t.update.subject.exists({
roleName:c.metadata.name,
subjectName:a
})) :g ? x(g, f, e) :w(c, f, e);
})) :g ? y(g, f, e) :x(c, f, e);
}
}), m.listAllRoles(n, {
errorNotification:!1
}).then(function(a) {
r = k.mapRolesForUI(_.first(a).by("metadata.name"), _.last(a).by("metadata.name"));
var b = k.sortRoles(r), c = k.filterRoles(r), e = function(a, b) {
s = k.mapRolesForUI(_.first(a).by("metadata.name"), _.last(a).by("metadata.name"));
var b = k.sortRoles(s), c = k.filterRoles(s), e = function(a, b) {
return _.some(b, {
metadata:{
name:a
}
});
};
v(), angular.extend(d, {
w(), angular.extend(d, {
toggle:{
roles:!1
},
Expand Down
Loading