Support enabling/disabling commentlink sections per-project
Allow project admins to customize which commentlinks apply to their section of the project hierarchy by selectively setting the enabled bit on each commentlink section. This allows site admins to create raw HTML commentlinks with less XSS risk (or at least more auditing) and not have them apply to every project. Change-Id: I8dd31aab98379f90253bbfdd9f669a7f5f78c25f
This commit is contained in:
@@ -75,6 +75,7 @@ public class ProjectConfig extends VersionedMetaData {
|
||||
private static final String KEY_MATCH = "match";
|
||||
private static final String KEY_HTML = "html";
|
||||
private static final String KEY_LINK = "link";
|
||||
private static final String KEY_ENABLED = "enabled";
|
||||
|
||||
private static final String PROJECT_CONFIG = "project.config";
|
||||
private static final String GROUP_LIST = "groups";
|
||||
@@ -162,19 +163,37 @@ public class ProjectConfig extends VersionedMetaData {
|
||||
public static CommentLinkInfo buildCommentLink(Config cfg, String name,
|
||||
boolean allowRaw) throws IllegalArgumentException {
|
||||
String match = cfg.getString(COMMENTLINK, name, KEY_MATCH);
|
||||
|
||||
// Unfortunately this validation isn't entirely complete. Clients
|
||||
// can have exceptions trying to evaluate the pattern if they don't
|
||||
// support a token used, even if the server does support the token.
|
||||
//
|
||||
// At the minimum, we can trap problems related to unmatched groups.
|
||||
Pattern.compile(match);
|
||||
if (match != null) {
|
||||
// Unfortunately this validation isn't entirely complete. Clients
|
||||
// can have exceptions trying to evaluate the pattern if they don't
|
||||
// support a token used, even if the server does support the token.
|
||||
//
|
||||
// At the minimum, we can trap problems related to unmatched groups.
|
||||
Pattern.compile(match);
|
||||
}
|
||||
|
||||
String link = cfg.getString(COMMENTLINK, name, KEY_LINK);
|
||||
String html = cfg.getString(COMMENTLINK, name, KEY_HTML);
|
||||
checkArgument(allowRaw || Strings.isNullOrEmpty(html),
|
||||
"Raw html replacement not allowed");
|
||||
return new CommentLinkInfo(name, match, link, html);
|
||||
boolean hasHtml = !Strings.isNullOrEmpty(html);
|
||||
|
||||
String rawEnabled = cfg.getString(COMMENTLINK, name, KEY_ENABLED);
|
||||
Boolean enabled;
|
||||
if (rawEnabled != null) {
|
||||
enabled = cfg.getBoolean(COMMENTLINK, name, KEY_ENABLED, true);
|
||||
} else {
|
||||
enabled = null;
|
||||
}
|
||||
checkArgument(allowRaw || !hasHtml, "Raw html replacement not allowed");
|
||||
|
||||
if (Strings.isNullOrEmpty(match) && Strings.isNullOrEmpty(link) && !hasHtml
|
||||
&& enabled != null) {
|
||||
if (enabled) {
|
||||
return new CommentLinkInfo.Enabled(name);
|
||||
} else {
|
||||
return new CommentLinkInfo.Disabled(name);
|
||||
}
|
||||
}
|
||||
return new CommentLinkInfo(name, match, link, html, enabled);
|
||||
}
|
||||
|
||||
public ProjectConfig(Project.NameKey projectName) {
|
||||
|
@@ -20,13 +20,37 @@ import com.google.common.base.Strings;
|
||||
|
||||
/** Info about a single commentlink section in a config. */
|
||||
public class CommentLinkInfo {
|
||||
public static class Enabled extends CommentLinkInfo {
|
||||
public Enabled(String name) {
|
||||
super(name, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isOverrideOnly() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Disabled extends CommentLinkInfo {
|
||||
public Disabled(String name) {
|
||||
super(name, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isOverrideOnly() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public final String match;
|
||||
public final String link;
|
||||
public final String html;
|
||||
public final Boolean enabled; // null means true
|
||||
|
||||
public transient final String name;
|
||||
|
||||
public CommentLinkInfo(String name, String match, String link, String html) {
|
||||
public CommentLinkInfo(String name, String match, String link, String html,
|
||||
Boolean enabled) {
|
||||
checkArgument(name != null, "invalid commentlink.name");
|
||||
checkArgument(!Strings.isNullOrEmpty(match),
|
||||
"invalid commentlink.%s.match", name);
|
||||
@@ -39,5 +63,30 @@ public class CommentLinkInfo {
|
||||
this.match = match;
|
||||
this.link = link;
|
||||
this.html = html;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
private CommentLinkInfo(CommentLinkInfo src, boolean enabled) {
|
||||
this.name = src.name;
|
||||
this.match = src.match;
|
||||
this.link = src.link;
|
||||
this.html = src.html;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
private CommentLinkInfo(String name, boolean enabled) {
|
||||
this.name = name;
|
||||
this.match = null;
|
||||
this.link = null;
|
||||
this.html = null;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
boolean isOverrideOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
CommentLinkInfo inherit(CommentLinkInfo src) {
|
||||
return new CommentLinkInfo(src, enabled);
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.ProvisionException;
|
||||
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
|
||||
@@ -40,7 +41,12 @@ public class CommentLinkProvider implements Provider<List<CommentLinkInfo>> {
|
||||
List<CommentLinkInfo> cls =
|
||||
Lists.newArrayListWithCapacity(subsections.size());
|
||||
for (String name : subsections) {
|
||||
cls.add(ProjectConfig.buildCommentLink(cfg, name, true));
|
||||
CommentLinkInfo cl = ProjectConfig.buildCommentLink(cfg, name, true);
|
||||
if (cl.isOverrideOnly()) {
|
||||
throw new ProvisionException(
|
||||
"commentlink " + name + " empty except for \"enabled\"");
|
||||
}
|
||||
cls.add(cl);
|
||||
}
|
||||
return ImmutableList.copyOf(cls);
|
||||
}
|
||||
|
@@ -382,7 +382,16 @@ public class ProjectState {
|
||||
}
|
||||
for (ProjectState s : treeInOrder()) {
|
||||
for (CommentLinkInfo cl : s.getConfig().getCommentLinkSections()) {
|
||||
cls.put(cl.name.toLowerCase(), cl);
|
||||
String name = cl.name.toLowerCase();
|
||||
if (cl.isOverrideOnly()) {
|
||||
CommentLinkInfo parent = cls.get(name);
|
||||
if (parent == null) {
|
||||
continue; // Ignore invalid overrides.
|
||||
}
|
||||
cls.put(name, cl.inherit(parent));
|
||||
} else {
|
||||
cls.put(name, cl);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImmutableList.copyOf(cls.values());
|
||||
|
Reference in New Issue
Block a user