Skip to content

Commit c3c6c40

Browse files
tulinkryVladimir Kotal
authored and
Vladimir Kotal
committed
targeting stacks for particular groups or projects (#1560)
1 parent d0b8e91 commit c3c6c40

File tree

4 files changed

+294
-36
lines changed

4 files changed

+294
-36
lines changed

src/org/opensolaris/opengrok/authorization/AuthorizationEntity.java

+159-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424

2525
import java.io.Serializable;
2626
import java.util.Map;
27+
import java.util.Set;
2728
import java.util.TreeMap;
29+
import java.util.TreeSet;
2830
import java.util.function.Predicate;
2931
import org.opensolaris.opengrok.configuration.Nameable;
3032

@@ -53,6 +55,52 @@
5355
*/
5456
public abstract class AuthorizationEntity implements Nameable, Serializable, Cloneable {
5557

58+
/**
59+
* Predicate specialized for the the plugin decisions. The caller should
60+
* implement the <code>decision</code> method. Returning true if the plugin
61+
* allows the action or false when the plugin forbids the action.
62+
*/
63+
public static abstract class PluginDecisionPredicate implements Predicate<IAuthorizationPlugin> {
64+
65+
@Override
66+
public boolean test(IAuthorizationPlugin t) {
67+
return decision(t);
68+
}
69+
70+
/**
71+
* Perform the authorization check for this plugin.
72+
*
73+
* @param t the plugin
74+
* @return true if plugin allows the action; false otherwise
75+
*/
76+
public abstract boolean decision(IAuthorizationPlugin t);
77+
78+
}
79+
80+
/**
81+
* Predicate specialized for the the entity skipping decisions. The caller
82+
* should implement the <code>shouldSkip</code> method. Returning true if
83+
* the entity should be skipped for this action and false if the entity
84+
* should be used.
85+
*/
86+
public static abstract class PluginSkippingPredicate implements Predicate<AuthorizationEntity> {
87+
88+
@Override
89+
public boolean test(AuthorizationEntity t) {
90+
return shouldSkip(t);
91+
}
92+
93+
/**
94+
* Decide if the entity should be skipped in this step of authorization.
95+
*
96+
* @param t the entity
97+
* @return true if skipped (authorization decision will not be affected
98+
* by this entity) or false if it should be used (authorization decision
99+
* will be affected by this entity)
100+
*/
101+
public abstract boolean shouldSkip(AuthorizationEntity t);
102+
}
103+
56104
private static final long serialVersionUID = 1L;
57105
/**
58106
* One of "required", "requisite", "sufficient".
@@ -61,6 +109,9 @@ public abstract class AuthorizationEntity implements Nameable, Serializable, Clo
61109
protected String name;
62110
protected Map<String, Object> setup = new TreeMap<>();
63111

112+
private Set<String> forProjects = new TreeSet<>();
113+
private Set<String> forGroups = new TreeSet<>();
114+
64115
protected transient boolean working = true;
65116

66117
public AuthorizationEntity() {
@@ -82,6 +133,8 @@ public AuthorizationEntity(AuthorizationEntity x) {
82133
name = x.name;
83134
setup = new TreeMap<>(x.setup);
84135
working = x.working;
136+
forGroups = new TreeSet<>(x.forGroups);
137+
forProjects = new TreeSet<>(x.forProjects);
85138
}
86139

87140
public AuthorizationEntity(AuthControlFlag flag, String name) {
@@ -111,12 +164,16 @@ public AuthorizationEntity(AuthControlFlag flag, String name) {
111164
*
112165
* @param entity the given entity - this is either group or project and is
113166
* passed just for the logging purposes.
114-
* @param predicate predicate returning true or false for the given entity
115-
* which determines if the authorization for such entity is successful or
116-
* failed
167+
* @param pluginPredicate predicate returning true or false for the given
168+
* entity which determines if the authorization for such entity is
169+
* successful or failed
170+
* @param skippingPredicate predicate returning true if this authorization
171+
* entity should be omitted from the authorization process
117172
* @return true if successful; false otherwise
118173
*/
119-
abstract public boolean isAllowed(Nameable entity, Predicate<IAuthorizationPlugin> predicate);
174+
abstract public boolean isAllowed(Nameable entity,
175+
PluginDecisionPredicate pluginPredicate,
176+
PluginSkippingPredicate skippingPredicate);
120177

121178
/**
122179
* Set the plugin to all classes which requires this class in the
@@ -201,6 +258,104 @@ public void setSetup(Map<String, Object> setup) {
201258
this.setup = setup;
202259
}
203260

261+
/**
262+
* Get the value of forProjects
263+
*
264+
* @return the value of forProjects
265+
*/
266+
public Set<String> forProjects() {
267+
return getForProjects();
268+
}
269+
270+
/**
271+
* Get the value of forProjects
272+
*
273+
* @return the value of forProjects
274+
*/
275+
public Set<String> getForProjects() {
276+
return forProjects;
277+
}
278+
279+
/**
280+
* Set the value of forProjects
281+
*
282+
* @param forProjects new value of forProjects
283+
*/
284+
public void setForProjects(Set<String> forProjects) {
285+
this.forProjects = forProjects;
286+
}
287+
288+
/**
289+
* Set the value of forProjects
290+
*
291+
* @param project add this project into the set
292+
*/
293+
public void setForProjects(String project) {
294+
this.forProjects.add(project);
295+
}
296+
297+
/**
298+
* Set the value of forProjects
299+
*
300+
* @param projects add all projects in this array into the set
301+
*
302+
* @see #setForProjects(java.lang.String)
303+
*/
304+
public void setForProjects(String[] projects) {
305+
for (String project : projects) {
306+
setForProjects(project);
307+
}
308+
}
309+
310+
/**
311+
* Get the value of forGroups
312+
*
313+
* @return the value of forGroups
314+
*/
315+
public Set<String> forGroups() {
316+
return getForGroups();
317+
}
318+
319+
/**
320+
* Get the value of forGroups
321+
*
322+
* @return the value of forGroups
323+
*/
324+
public Set<String> getForGroups() {
325+
return forGroups;
326+
}
327+
328+
/**
329+
* Set the value of forGroups
330+
*
331+
* @param forGroups new value of forGroups
332+
*/
333+
public void setForGroups(Set<String> forGroups) {
334+
this.forGroups = forGroups;
335+
}
336+
337+
/**
338+
* Set the value of forGroups
339+
*
340+
* @param group add this group into the set
341+
*/
342+
public void setForGroups(String group) {
343+
this.forGroups.add(group);
344+
}
345+
346+
/**
347+
* Set the value of forGroups
348+
*
349+
* @param groups add all groups in this array into the set
350+
*
351+
* @see #setForGroups(java.lang.String)
352+
*/
353+
public void setForGroups(String[] groups) {
354+
for (String group : groups) {
355+
setForGroups(group);
356+
}
357+
}
358+
204359
/**
205360
* Check if the plugin exists and has not failed while loading.
206361
*

src/org/opensolaris/opengrok/authorization/AuthorizationFramework.java

+41-11
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import java.util.TreeMap;
3636
import java.util.concurrent.locks.ReadWriteLock;
3737
import java.util.concurrent.locks.ReentrantReadWriteLock;
38-
import java.util.function.Predicate;
3938
import java.util.jar.JarEntry;
4039
import java.util.jar.JarFile;
4140
import java.util.logging.Level;
@@ -145,11 +144,26 @@ public boolean isAllowed(HttpServletRequest request, Project project) {
145144
request,
146145
"plugin_framework_project_cache",
147146
project,
148-
new Predicate<IAuthorizationPlugin>() {
147+
new AuthorizationEntity.PluginDecisionPredicate() {
149148
@Override
150-
public boolean test(IAuthorizationPlugin plugin) {
149+
public boolean decision(IAuthorizationPlugin plugin) {
151150
return plugin.isAllowed(request, project);
152151
}
152+
}, new AuthorizationEntity.PluginSkippingPredicate() {
153+
@Override
154+
public boolean shouldSkip(AuthorizationEntity authEntity) {
155+
// shouldn't skip if there is no setup
156+
if (authEntity.forProjects().isEmpty() && authEntity.forGroups().isEmpty()) {
157+
return false;
158+
}
159+
160+
// shouldn't skip if the project is contained in the setup
161+
if (authEntity.forProjects().contains(project.getName())) {
162+
return false;
163+
}
164+
165+
return true;
166+
}
153167
});
154168
}
155169

@@ -168,11 +182,22 @@ public boolean isAllowed(HttpServletRequest request, Group group) {
168182
request,
169183
"plugin_framework_group_cache",
170184
group,
171-
new Predicate<IAuthorizationPlugin>() {
185+
new AuthorizationEntity.PluginDecisionPredicate() {
172186
@Override
173-
public boolean test(IAuthorizationPlugin plugin) {
187+
public boolean decision(IAuthorizationPlugin plugin) {
174188
return plugin.isAllowed(request, group);
175189
}
190+
}, new AuthorizationEntity.PluginSkippingPredicate() {
191+
@Override
192+
public boolean shouldSkip(AuthorizationEntity authEntity) {
193+
// shouldn't skip if there is no setup
194+
if (authEntity.forProjects().isEmpty() && authEntity.forGroups().isEmpty()) {
195+
return false;
196+
}
197+
198+
// shouldn't skip if the group is contained in the setup
199+
return !authEntity.forGroups().contains(group.getName());
200+
}
176201
});
177202
}
178203

@@ -579,7 +604,7 @@ public void setPluginVersion(int pluginVersion) {
579604
* <p>
580605
* The order of plugin invokation is given by the configuration
581606
* {@link RuntimeEnvironment#getPluginStack()} and appropriate actions are
582-
* taken when traversing the list with set of keywords, such as:</p>
607+
* taken when traversing the stack with set of keywords, such as:</p>
583608
*
584609
* <h4>required</h4>
585610
* Failure of such a plugin will ultimately lead to the authorization
@@ -617,7 +642,8 @@ public void setPluginVersion(int pluginVersion) {
617642
*/
618643
@SuppressWarnings("unchecked")
619644
private boolean checkAll(HttpServletRequest request, String cache, Nameable entity,
620-
Predicate<IAuthorizationPlugin> predicate) {
645+
AuthorizationEntity.PluginDecisionPredicate pluginPredicate,
646+
AuthorizationEntity.PluginSkippingPredicate skippingPredicate) {
621647
if (stack == null) {
622648
return true;
623649
}
@@ -639,7 +665,7 @@ private boolean checkAll(HttpServletRequest request, String cache, Nameable enti
639665

640666
long time = System.currentTimeMillis();
641667

642-
boolean overallDecision = performCheck(entity, predicate);
668+
boolean overallDecision = performCheck(entity, pluginPredicate, skippingPredicate);
643669

644670
time = System.currentTimeMillis() - time;
645671

@@ -663,14 +689,18 @@ private boolean checkAll(HttpServletRequest request, String cache, Nameable enti
663689
* Perform the actual check for the entity.
664690
*
665691
* @param entity either a project or a group
666-
* @param predicate a predicate that decides if the authorization is
692+
* @param pluginPredicate a predicate that decides if the authorization is
667693
* successful for the given plugin
694+
* @param skippingPredicate predicate that decides if given authorization
695+
* entity should be omitted from the authorization process
668696
* @return true if entity is allowed; false otherwise
669697
*/
670-
private boolean performCheck(Nameable entity, Predicate<IAuthorizationPlugin> predicate) {
698+
private boolean performCheck(Nameable entity,
699+
AuthorizationEntity.PluginDecisionPredicate pluginPredicate,
700+
AuthorizationEntity.PluginSkippingPredicate skippingPredicate) {
671701
lock.readLock().lock();
672702
try {
673-
return stack.isAllowed(entity, predicate);
703+
return stack.isAllowed(entity, pluginPredicate, skippingPredicate);
674704
} finally {
675705
lock.readLock().unlock();
676706
}

src/org/opensolaris/opengrok/authorization/AuthorizationPlugin.java

+24-6
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
import java.util.Map;
2626
import java.util.TreeMap;
27-
import java.util.function.Predicate;
2827
import java.util.logging.Level;
2928
import java.util.logging.Logger;
3029
import javax.servlet.http.HttpServletRequest;
@@ -99,6 +98,12 @@ public synchronized void load(Map<String, Object> parameters) {
9998
+ "This can cause the authorization to fail always.",
10099
getName());
101100
setFailed();
101+
LOGGER.log(Level.INFO, "[{0}] Plugin \"{1}\" {2} and is {3}.",
102+
new Object[]{
103+
getFlag().toString().toUpperCase(),
104+
getName(),
105+
hasPlugin() ? "found" : "not found",
106+
isWorking() ? "working" : "failed"});
102107
return;
103108
}
104109

@@ -147,9 +152,11 @@ public synchronized void unload() {
147152
*
148153
* @param entity the given entity - this is either group or project and is
149154
* passed just for the logging purposes.
150-
* @param predicate predicate returning true or false for the given entity
151-
* which determines if the authorization for such entity is successful or
152-
* failed for particular request and plugin
155+
* @param pluginPredicate predicate returning true or false for the given
156+
* entity which determines if the authorization for such entity is
157+
* successful or failed for particular request and plugin
158+
* @param skippingPredicate predicate returning true if this authorization
159+
* entity should be omitted from the authorization process
153160
* @return true if the plugin is not failed and the project is allowed;
154161
* false otherwise
155162
*
@@ -158,11 +165,22 @@ public synchronized void unload() {
158165
* @see IAuthorizationPlugin#isAllowed(HttpServletRequest, Group)
159166
*/
160167
@Override
161-
public boolean isAllowed(Nameable entity, Predicate<IAuthorizationPlugin> predicate) {
168+
public boolean isAllowed(Nameable entity,
169+
AuthorizationEntity.PluginDecisionPredicate pluginPredicate,
170+
AuthorizationEntity.PluginSkippingPredicate skippingPredicate) {
171+
/**
172+
* We don't check the skippingPredicate here as this instance is
173+
* <b>always</b> a part of some stack (may be the default stack) and the
174+
* stack checks the skipping predicate before invoking this method.
175+
*
176+
* @see AuthorizationStack#processStack
177+
*/
178+
162179
if (isFailed()) {
163180
return false;
164181
}
165-
return predicate.test(plugin);
182+
183+
return pluginPredicate.decision(this.plugin);
166184
}
167185

168186
/**

0 commit comments

Comments
 (0)