提交 148ed0d4 编写于 作者: K Kohsuke Kawaguchi

Merge branch 'pull-628'

......@@ -25,6 +25,8 @@
*/
package hudson;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import hudson.cli.CLICommand;
import hudson.console.ConsoleAnnotationDescriptor;
import hudson.console.ConsoleAnnotatorFactory;
......@@ -33,6 +35,7 @@ import hudson.model.ParameterDefinition.ParameterDescriptor;
import hudson.search.SearchableModelObject;
import hudson.security.AccessControlled;
import hudson.security.AuthorizationStrategy;
import hudson.security.GlobalSecurityConfiguration;
import hudson.security.Permission;
import hudson.security.SecurityRealm;
import hudson.security.captcha.CaptchaSupport;
......@@ -57,6 +60,8 @@ import hudson.views.MyViewsTabBar;
import hudson.views.ViewsTabBar;
import hudson.widgets.RenderOnDemandClosure;
import jenkins.model.GlobalConfiguration;
import jenkins.model.GlobalConfigurationCategory;
import jenkins.model.GlobalConfigurationCategory.Unclassified;
import jenkins.model.Jenkins;
import jenkins.model.ModelObjectWithContextMenu;
import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
......@@ -74,7 +79,6 @@ import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.jelly.InternationalizedStringExpression.RawHtmlArgument;
import javax.management.modelmbean.DescriptorSupport;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
......@@ -769,32 +773,11 @@ public class Functions {
* Perhaps it is better to introduce another annotation element? But then,
* extensions shouldn't normally concern themselves about ordering too much, and the only reason
* we needed this for {@link GlobalConfiguration}s are for backward compatibility.
*
* @param predicate
* Filter the descriptors based on {@link GlobalConfigurationCategory}
*/
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfig() {
class Tag implements Comparable<Tag> {
double ordinal;
String hierarchy;
Descriptor d;
Tag(double ordinal, Descriptor d) {
this.ordinal = ordinal;
this.d = d;
this.hierarchy = buildSuperclassHierarchy(d.clazz, new StringBuilder()).toString();
}
private StringBuilder buildSuperclassHierarchy(Class c, StringBuilder buf) {
Class sc = c.getSuperclass();
if (sc!=null) buildSuperclassHierarchy(sc,buf).append(':');
return buf.append(c.getName());
}
public int compareTo(Tag that) {
int r = Double.compare(this.ordinal, that.ordinal);
if (r!=0) return -r; // descending for ordinal
return this.hierarchy.compareTo(that.hierarchy);
}
}
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfig(Predicate<GlobalConfigurationCategory> predicate) {
ExtensionList<Descriptor> exts = Jenkins.getInstance().getExtensionList(Descriptor.class);
List<Tag> r = new ArrayList<Tag>(exts.size());
......@@ -802,7 +785,13 @@ public class Functions {
Descriptor d = c.getInstance();
if (d.getGlobalConfigPage()==null) continue;
r.add(new Tag(d instanceof GlobalConfiguration ? c.ordinal() : 0, d));
if (d instanceof GlobalConfiguration) {
if (predicate.apply(((GlobalConfiguration)d).getCategory()))
r.add(new Tag(c.ordinal(), d));
} else {
if (predicate.apply(GlobalConfigurationCategory.get(Unclassified.class)))
r.add(new Tag(0, d));
}
}
Collections.sort(r);
......@@ -812,7 +801,37 @@ public class Functions {
return DescriptorVisibilityFilter.apply(Jenkins.getInstance(),answer);
}
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfig() {
return getSortedDescriptorsForGlobalConfig(Predicates.<GlobalConfigurationCategory>alwaysTrue());
}
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfigNoSecurity() {
return getSortedDescriptorsForGlobalConfig(Predicates.not(GlobalSecurityConfiguration.FILTER));
}
private static class Tag implements Comparable<Tag> {
double ordinal;
String hierarchy;
Descriptor d;
Tag(double ordinal, Descriptor d) {
this.ordinal = ordinal;
this.d = d;
this.hierarchy = buildSuperclassHierarchy(d.clazz, new StringBuilder()).toString();
}
private StringBuilder buildSuperclassHierarchy(Class c, StringBuilder buf) {
Class sc = c.getSuperclass();
if (sc!=null) buildSuperclassHierarchy(sc,buf).append(':');
return buf.append(c.getName());
}
public int compareTo(Tag that) {
int r = Double.compare(this.ordinal, that.ordinal);
if (r!=0) return -r; // descending for ordinal
return this.hierarchy.compareTo(that.hierarchy);
}
}
/**
* Computes the path to the icon of the given action
* from the context path.
......
......@@ -23,47 +23,74 @@
*/
package hudson.security;
import com.google.common.base.Predicate;
import hudson.BulkChange;
import hudson.Extension;
import hudson.Functions;
import hudson.markup.MarkupFormatter;
import jenkins.model.GlobalConfiguration;
import hudson.model.Descriptor;
import hudson.model.Descriptor.FormException;
import hudson.model.ManagementLink;
import hudson.util.FormApply;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import jenkins.model.GlobalConfigurationCategory;
import jenkins.model.Jenkins;
import jenkins.util.ServerTcpPort;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
import java.io.IOException;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
/**
* Security configuration.
*
* @author Kohsuke Kawaguchi
*/
@Extension(ordinal=200)
public class GlobalSecurityConfiguration extends GlobalConfiguration {
@Extension(ordinal = Integer.MAX_VALUE - 210)
public class GlobalSecurityConfiguration extends ManagementLink {
private static final Logger LOGGER = Logger.getLogger(GlobalSecurityConfiguration.class.getName());
public MarkupFormatter getMarkupFormatter() {
return Jenkins.getInstance().getMarkupFormatter();
}
public int getSlaveAgentPort() {
return Jenkins.getInstance().getSlaveAgentPort();
}
@Override
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
public synchronized void doConfigure(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, FormException {
// for compatibility reasons, the actual value is stored in Jenkins
Jenkins j = Jenkins.getInstance();
BulkChange bc = new BulkChange(Jenkins.getInstance());
try{
boolean result = configure(req, req.getSubmittedForm());
LOGGER.log(Level.FINE, "security saved: "+result);
Jenkins.getInstance().save();
FormApply.success(req.getContextPath()+"/manage").generateResponse(req, rsp, null);
} finally {
bc.commit();
}
}
public boolean configure(StaplerRequest req, JSONObject json) throws hudson.model.Descriptor.FormException {
// for compatibility reasons, the actual value is stored in Jenkins
Jenkins j = Jenkins.getInstance();
j.checkPermission(Jenkins.ADMINISTER);
if (json.has("useSecurity")) {
JSONObject security = json.getJSONObject("useSecurity");
j.setSecurityRealm(SecurityRealm.all().newInstanceFromRadioList(security, "realm"));
j.setAuthorizationStrategy(AuthorizationStrategy.all().newInstanceFromRadioList(security, "authorization"));
try {
j.setSlaveAgentPort(new ServerTcpPort(security.getJSONObject("slaveAgentPort")).getPort());
} catch (IOException e) {
throw new FormException(e,"slaveAgentPortType");
throw new hudson.model.Descriptor.FormException(e, "slaveAgentPortType");
}
if (security.has("markupFormatter")) {
j.setMarkupFormatter(req.bindJSON(MarkupFormatter.class, security.getJSONObject("markupFormatter")));
} else {
......@@ -73,7 +100,51 @@ public class GlobalSecurityConfiguration extends GlobalConfiguration {
j.disableSecurity();
}
return true;
// persist all the additional security configs
boolean result = true;
for(Descriptor<?> d : Functions.getSortedDescriptorsForGlobalConfig(FILTER)){
result &= configureDescriptor(req,json,d);
}
return result;
}
private boolean configureDescriptor(StaplerRequest req, JSONObject json, Descriptor<?> d) throws FormException {
// collapse the structure to remain backward compatible with the JSON structure before 1.
String name = d.getJsonSafeClassName();
JSONObject js = json.has(name) ? json.getJSONObject(name) : new JSONObject(); // if it doesn't have the property, the method returns invalid null object.
json.putAll(js);
return d.configure(req, js);
}
@Override
public String getDisplayName() {
return Messages.GlobalSecurityConfiguration_DisplayName();
}
@Override
public String getDescription() {
return Messages.GlobalSecurityConfiguration_Description();
}
@Override
public String getIconFileName() {
return "secure.png";
}
@Override
public String getUrlName() {
return "configureSecurity";
}
@Override
public Permission getRequiredPermission() {
return Jenkins.ADMINISTER;
}
}
public static Predicate<GlobalConfigurationCategory> FILTER = new Predicate<GlobalConfigurationCategory>() {
public boolean apply(GlobalConfigurationCategory input) {
return input instanceof GlobalConfigurationCategory.Security;
}
};
}
......@@ -25,6 +25,7 @@ package hudson.security.csrf;
import hudson.Extension;
import jenkins.model.GlobalConfiguration;
import jenkins.model.GlobalConfigurationCategory;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
......@@ -36,6 +37,11 @@ import org.kohsuke.stapler.StaplerRequest;
*/
@Extension(ordinal=195) // immediately after the security setting
public class GlobalCrumbIssuerConfiguration extends GlobalConfiguration {
@Override
public GlobalConfigurationCategory getCategory() {
return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class);
}
@Override
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
// for compatibility reasons, the actual value is stored in Jenkins
......
......@@ -31,6 +31,15 @@ public abstract class GlobalConfiguration extends Descriptor<GlobalConfiguration
return this;
}
/**
* Every {@link GlobalConfiguration} belongs to a specific category.
*
* @return never null, always the same value for a given instance of {@link GlobalConfiguration}.
*/
public GlobalConfigurationCategory getCategory() {
return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Unclassified.class);
}
/**
* Unless this object has additional web presence, display name is not used at all.
* So default to "".
......
package jenkins.model;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.ModelObject;
import hudson.security.*;
import hudson.security.Messages;
/**
* Grouping of related {@link GlobalConfiguration}s.
*
* <p>
* To facilitate the separation of the global configuration into multiple pages, tabs, and so on,
* {@link GlobalConfiguration}s are classified into categories (such as "security", "tools", as well
* as the catch all "unclassified".) Categories themselves are extensible &mdash; plugins may introduce
* its own category as well, although that should only happen if you are creating a big enough subsystem.
*
* <p>
* The primary purpose of this is to enable future UIs to split the global configurations to
* smaller pieces that can be individually looked at and updated.
*
* @author Kohsuke Kawaguchi
* @since 1.494
* @see GlobalConfiguration
*/
public abstract class GlobalConfigurationCategory implements ExtensionPoint, ModelObject {
/**
* One-line plain text message that explains what this category is about.
* This can be used in the UI to help the user pick the right category.
*
* The text should be longer than {@link #getDisplayName()}
*/
public abstract String getShortDescription();
/**
* Returns all the registered {@link GlobalConfiguration} descriptors.
*/
public static ExtensionList<GlobalConfigurationCategory> all() {
return Jenkins.getInstance().getExtensionList(GlobalConfigurationCategory.class);
}
public static <T extends GlobalConfigurationCategory> T get(Class<T> type) {
return all().get(type);
}
/**
* This category represents the catch-all I-dont-know-what-category-it-is instance,
* used for those {@link GlobalConfiguration}s that don't really deserve/need a separate
* category.
*
* Also used for backward compatibility. All {@link GlobalConfiguration}s without
* explicit category gets this as the category.
*
* In the current UI, this corresponds to the /configure link.
*/
@Extension
public static class Unclassified extends GlobalConfigurationCategory {
@Override
public String getShortDescription() {
return jenkins.management.Messages.ConfigureLink_Description();
}
public String getDisplayName() {
return jenkins.management.Messages.ConfigureLink_DisplayName();
}
}
/**
* Security related configurations.
*/
@Extension
public static class Security extends GlobalConfigurationCategory {
@Override
public String getShortDescription() {
return Messages.GlobalSecurityConfiguration_Description();
}
public String getDisplayName() {
return hudson.security.Messages.GlobalSecurityConfiguration_DisplayName();
}
}
}
package hudson.security.GlobalSecurityConfiguration
import hudson.security.SecurityRealm
import hudson.security.AuthorizationStrategy
def f=namespace(lib.FormTagLib)
f.optionalBlock( field:"useSecurity", title:_("Enable security"), checked:app.useSecurity) {
f.entry (title:_("TCP port for JNLP slave agents"), field:"slaveAgentPort") {
f.serverTcpPort()
}
f.dropdownDescriptorSelector(title:_("Markup Formatter"),field:"markupFormatter")
f.entry(title:_("Access Control")) {
table(style:"width:100%") {
f.descriptorRadioList(title:_("Security Realm"),varName:"realm", instance:app.securityRealm, descriptors:SecurityRealm.all())
f.descriptorRadioList(title:_("Authorization"), varName:"authorization", instance:app.authorizationStrategy, descriptors:AuthorizationStrategy.all())
}
}
}
package hudson.security.GlobalSecurityConfiguration
import hudson.security.SecurityRealm
import hudson.markup.MarkupFormatterDescriptor
import hudson.security.AuthorizationStrategy
import jenkins.model.GlobalConfiguration
import hudson.Functions
import hudson.model.Descriptor
def f=namespace(lib.FormTagLib)
def l=namespace(lib.LayoutTagLib)
def st=namespace("jelly:stapler")
l.layout(norefresh:true, permission:app.ADMINISTER, title:my.displayName) {
l.main_panel {
h1 {
img(src:"${imagesURL}/48x48/secure.png", height:48,width:48)
text(my.displayName)
}
p()
div(class:"behavior-loading", _("LOADING"))
f.form(method:"post",name:"config",action:"configure") {
set("instance",my);
f.optionalBlock( field:"useSecurity", title:_("Enable security"), checked:app.useSecurity) {
f.entry (title:_("TCP port for JNLP slave agents"), field:"slaveAgentPort") {
f.serverTcpPort()
}
f.dropdownDescriptorSelector(title:_("Markup Formatter"),descriptors: MarkupFormatterDescriptor.all(), field: 'markupFormatter')
f.entry(title:_("Access Control")) {
table(style:"width:100%") {
f.descriptorRadioList(title:_("Security Realm"),varName:"realm", instance:app.securityRealm, descriptors:SecurityRealm.all())
f.descriptorRadioList(title:_("Authorization"), varName:"authorization", instance:app.authorizationStrategy, descriptors:AuthorizationStrategy.all())
}
}
}
Functions.getSortedDescriptorsForGlobalConfig(my.FILTER).each { Descriptor descriptor ->
set("descriptor",descriptor)
set("instance",descriptor)
f.rowSet(name:descriptor.jsonSafeClassName) {
st.include(from:descriptor, page:descriptor.globalConfigPage)
}
}
f.block {
div(id:"bottom-sticker") {
div(class:"bottom-sticker-inner") {
f.submit(value:_("Save"))
f.apply()
}
}
}
}
}
}
......@@ -19,6 +19,8 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
GlobalSecurityConfiguration.DisplayName=Configure Global Security
GlobalSecurityConfiguration.Description=Secure Jenkins; define who is allowed to access/use the system.
GlobalMatrixAuthorizationStrategy.DisplayName=Matrix-based security
......
......@@ -53,7 +53,7 @@ THE SOFTWARE.
</f:entry>
<!-- global configuration from everyone -->
<j:forEach var="descriptor" items="${h.getSortedDescriptorsForGlobalConfig()}">
<j:forEach var="descriptor" items="${h.getSortedDescriptorsForGlobalConfigNoSecurity()}">
<j:set var="instance" value="${descriptor}" /><!-- this makes the <f:textbox field=.../> work -->
<f:rowSet name="${descriptor.jsonSafeClassName}">
<st:include page="${descriptor.globalConfigPage}" from="${descriptor}" />
......
......@@ -24,6 +24,7 @@
*/
package org.jvnet.hudson.test;
import com.gargoylesoftware.htmlunit.AlertHandler;
import com.gargoylesoftware.htmlunit.html.HtmlImage;
import com.google.inject.Injector;
import hudson.ClassicPluginStrategy;
......@@ -1659,6 +1660,12 @@ public abstract class HudsonTestCase extends TestCase implements RootAction {
}
});
setAlertHandler(new AlertHandler() {
public void handleAlert(Page page, String message) {
throw new AssertionError("Alert dialog poped up: "+message);
}
});
// avoid a hang by setting a time out. It should be long enough to prevent
// false-positive timeout on slow systems
setTimeout(60*1000);
......
此差异由.gitattributes 抑制。
此差异由.gitattributes 抑制。
此差异由.gitattributes 抑制。
此差异由.gitattributes 抑制。
此差异由.gitattributes 抑制。
此差异由.gitattributes 抑制。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册