forked from openshift/origin-web-common
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathauthorizationService.js
161 lines (146 loc) · 6.46 KB
/
authorizationService.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
'use strict';
angular.module("openshiftCommonServices")
.factory("AuthorizationService", function($q, $cacheFactory, Logger, $interval, APIService, DataService){
var currentProject = null;
var cachedRulesByProject = $cacheFactory('rulesCache', {
number: 10
});
// Permisive mode will cause no checks to be done for the user actions.
var permissiveMode = false;
var REVIEW_RESOURCES = ["localresourceaccessreviews", "localsubjectaccessreviews", "resourceaccessreviews", "selfsubjectaccessreviews", "selfsubjectrulesreviews", "subjectaccessreviews"];
// Transform data from:
// rules = {resources: ["jobs"], apiGroups: ["extensions"], verbs:["create","delete","get","list","update"]}
// into:
// normalizedRules = {"extensions": {"jobs": ["create","delete","get","list","update"]}}
var normalizeRules = function(rules) {
var normalizedRules = {};
_.each(rules, function(rule) {
_.each(rule.apiGroups, function(apiGroup) {
if (!normalizedRules[apiGroup]) {
normalizedRules[apiGroup] = {};
}
_.each(rule.resources, function(resource) {
normalizedRules[apiGroup][resource] = rule.verbs;
});
});
});
return normalizedRules;
};
// 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
var checkResource = function(resource) {
if (resource === "projectrequests" || _.includes(resource, "/") || _.includes(REVIEW_RESOURCES, resource)) {
return false;
} else {
return true;
}
};
// Check if user can create/update any resource on the 'Add to project' so the button will be displayed.
var canAddToProjectCheck = function(rules) {
return _.some(rules, function(rule) {
return _.some(rule.resources, function(resource) {
return checkResource(resource) && !_.isEmpty(_.intersection(rule.verbs ,(["*", "create", "update"])));
});
});
};
// Avoid loading rules twice if another request is already in flight. Key
// is the project name, value is the promise.
var inFlightRulesRequests = {};
// 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 || forceRefresh) {
// Check if APIserver contains 'selfsubjectrulesreviews' resource. If not switch to permissive mode.
if (APIService.apiInfo(rulesResource)) {
// If a request is already in flight, return the promise for that request.
if (inFlightRulesRequests[projectName]) {
return inFlightRulesRequests[projectName];
}
Logger.log("AuthorizationService, loading user rules for " + projectName + " project");
inFlightRulesRequests[projectName] = deferred.promise;
var resourceGroupVersion = {
kind: "SelfSubjectRulesReview",
apiVersion: "v1"
};
DataService.create(rulesResource, null, resourceGroupVersion, {namespace: projectName}).then(
function(data) {
var normalizedData = normalizeRules(data.status.rules);
var canUserAddToProject = canAddToProjectCheck(data.status.rules);
cachedRulesByProject.put(projectName, {rules: normalizedData,
canAddToProject: canUserAddToProject,
forceRefresh: false,
cacheTimestamp: _.now()
});
deferred.resolve();
}, function() {
permissiveMode = true;
deferred.resolve();
}).finally(function() {
delete inFlightRulesRequests[projectName];
});
} else {
Logger.log("AuthorizationService, resource 'selfsubjectrulesreviews' is not part of APIserver. Switching into permissive mode.");
permissiveMode = true;
deferred.resolve();
}
} else {
// Using cached data.
Logger.log("AuthorizationService, using cached rules for " + projectName + " project");
if ((_.now() - projectRules.cacheTimestamp) >= 600000) {
projectRules.forceRefresh = true;
}
deferred.resolve();
}
return deferred.promise;
};
var getRulesForProject = function(projectName) {
return _.get(cachedRulesByProject.get(projectName || currentProject), ['rules']);
};
// _canI checks whether any rule allows the specified verb (directly or via a wildcard verb) on the literal group and resource.
var _canI = function(rules, verb, group, resource) {
var resources = rules[group];
if (!resources) {
return false;
}
var verbs = resources[resource];
if (!verbs) {
return false;
}
return _.includes(verbs, verb) || _.includes(verbs, '*');
};
// canI checks whether any rule allows the specified verb on the specified group-resource (directly or via a wildcard rule).
var canI = function(resource, verb, projectName) {
if (permissiveMode) {
return true;
}
// normalize to structured form
var r = APIService.toResourceGroupVersion(resource);
var rules = getRulesForProject(projectName || currentProject);
if (!rules) {
return false;
}
return _canI(rules, verb, r.group, r.resource) ||
_canI(rules, verb, '*', '*' ) ||
_canI(rules, verb, r.group, '*' ) ||
_canI(rules, verb, '*', r.resource);
};
var canIAddToProject = function(projectName) {
if (permissiveMode) {
return true;
} else {
return !!_.get(cachedRulesByProject.get(projectName || currentProject), ['canAddToProject']);
}
};
return {
checkResource: checkResource,
getProjectRules: getProjectRules,
canI: canI,
canIAddToProject: canIAddToProject,
getRulesForProject: getRulesForProject
};
});