From cc12964e9fbec5350e2a3f1339d4c00de2b57b15 Mon Sep 17 00:00:00 2001 From: kohsuke Date: Thu, 29 Jan 2009 23:52:11 +0000 Subject: [PATCH] Matrix security configuration now validates whether the username/password are valid. git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@14887 71c3de6d-444a-0410-be80-ed276b4c234a --- .../security/AuthorizationMatrixProperty.java | 9 ++- .../GlobalMatrixAuthorizationStrategy.java | 64 +++++++++++++++++-- .../security/HudsonPrivateSecurityRealm.java | 9 +++ .../java/hudson/security/SecurityRealm.java | 12 ++++ .../config.jelly | 8 ++- .../main/resources/lib/layout/layout.jelly | 1 + war/resources/scripts/hudson-behavior.js | 15 ++++- 7 files changed, 109 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/hudson/security/AuthorizationMatrixProperty.java b/core/src/main/java/hudson/security/AuthorizationMatrixProperty.java index d9025873d7..d3a7b6ef1f 100644 --- a/core/src/main/java/hudson/security/AuthorizationMatrixProperty.java +++ b/core/src/main/java/hudson/security/AuthorizationMatrixProperty.java @@ -14,11 +14,13 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; +import java.io.IOException; import net.sf.json.JSONObject; import org.acegisecurity.acls.sid.Sid; import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.QueryParameter; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.MarshallingContext; @@ -26,6 +28,8 @@ import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import javax.servlet.ServletException; + /** * {@link JobProperty} to associate ACL for each project. */ @@ -94,7 +98,6 @@ public class AuthorizationMatrixProperty extends JobProperty> { } public static class DescriptorImpl extends JobPropertyDescriptor { - @Override public JobProperty newInstance(StaplerRequest req, JSONObject formData) throws FormException { @@ -135,6 +138,10 @@ public class AuthorizationMatrixProperty extends JobProperty> { public boolean showPermission(Permission p) { return p!=Item.CREATE; } + + public void doCheckName(@QueryParameter String value) throws IOException, ServletException { + GlobalMatrixAuthorizationStrategy.DESCRIPTOR.doCheckName(value); + } } private final class AclImpl extends SidACL { diff --git a/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java b/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java index 522dae37a3..6c3cfa46f3 100644 --- a/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java +++ b/core/src/main/java/hudson/security/GlobalMatrixAuthorizationStrategy.java @@ -6,13 +6,17 @@ import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import hudson.model.Descriptor; +import hudson.model.Hudson; +import hudson.util.FormFieldValidator; +import hudson.Functions; import net.sf.json.JSONObject; -import org.acegisecurity.Authentication; -import org.acegisecurity.acls.sid.GrantedAuthoritySid; -import org.acegisecurity.acls.sid.PrincipalSid; +import org.acegisecurity.userdetails.UsernameNotFoundException; import org.acegisecurity.acls.sid.Sid; import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.QueryParameter; +import org.springframework.dao.DataAccessException; +import javax.servlet.ServletException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -21,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.io.IOException; /** * Role-based authorization via a matrix. @@ -126,7 +131,7 @@ public class GlobalMatrixAuthorizationStrategy extends AuthorizationStrategy { return DESCRIPTOR; } - public static final Descriptor DESCRIPTOR = new DescriptorImpl(); + public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); /** * Persist {@link GlobalMatrixAuthorizationStrategy} as a list of IDs that @@ -216,6 +221,57 @@ public class GlobalMatrixAuthorizationStrategy extends AuthorizationStrategy { public boolean showPermission(Permission p) { return true; } + + public void doCheckName(@QueryParameter String value ) throws IOException, ServletException { + final String v = value.substring(1,value.length()-1); + new FormFieldValidator(Hudson.ADMINISTER) { + protected void check() throws IOException, ServletException { + SecurityRealm sr = Hudson.getInstance().getSecurityRealm(); + String ev = Functions.escape(v); + + if(v.equals("authenticated")) { + // systerm reserved group + respond(""+ makeImg("user.gif") +ev+""); + return; + } + + try { + sr.loadUserByUsername(v); + respond(""+ makeImg("person.gif") +ev+""); + return; + } catch (UserMayOrMayNotExistException e) { + // undecidable, meaning the user may exist + respond(""+ev+""); + return; + } catch (UsernameNotFoundException e) { + // fall through next + } catch (DataAccessException e) { + // fall through next + } + + try { + sr.loadGroupByGroupname(v); + respond(""+ makeImg("user.gif") +ev+""); + return; + } catch (UserMayOrMayNotExistException e) { + // undecidable, meaning the group may exist + respond(""+ev+""); + return; + } catch (UsernameNotFoundException e) { + // fall through next + } catch (DataAccessException e) { + // fall through next + } + + // couldn't find it. it doesn't exit + respond(""+ makeImg("error.gif") +ev+""); + } + }.process(); + } + + private String makeImg(String gif) { + return String.format("", Hudson.RESOURCE_PATH, gif); + } } } diff --git a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java index 117f9e8917..4bc162aead 100644 --- a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java +++ b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java @@ -27,6 +27,7 @@ import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.DataBoundConstructor; import org.springframework.web.context.WebApplicationContext; +import org.springframework.dao.DataAccessException; import javax.servlet.ServletException; import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; @@ -61,6 +62,14 @@ public class HudsonPrivateSecurityRealm extends SecurityRealm implements ModelOb return !disableSignup; } + /** + * This implementation doesn't support groups. + */ + @Override + public GroupDetails loadGroupByGroupname(String groupname) throws UsernameNotFoundException, DataAccessException { + throw new UsernameNotFoundException(groupname); + } + @Override public SecurityComponents createSecurityComponents() { BeanBuilder builder = new BeanBuilder(); diff --git a/core/src/main/java/hudson/security/SecurityRealm.java b/core/src/main/java/hudson/security/SecurityRealm.java index d0c609ce7e..53a31f55ea 100644 --- a/core/src/main/java/hudson/security/SecurityRealm.java +++ b/core/src/main/java/hudson/security/SecurityRealm.java @@ -286,6 +286,10 @@ public abstract class SecurityRealm implements Describable, Exten public Authentication authenticate(Authentication authentication) { return authentication; } + }, new UserDetailsService() { + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { + throw new UsernameNotFoundException(username); + } }); } @@ -297,6 +301,14 @@ public abstract class SecurityRealm implements Describable, Exten return null; } + /** + * There's no group. + */ + @Override + public GroupDetails loadGroupByGroupname(String groupname) throws UsernameNotFoundException, DataAccessException { + throw new UsernameNotFoundException(groupname); + } + /** * We don't need any filter for this {@link SecurityRealm}. */ diff --git a/core/src/main/resources/hudson/security/GlobalMatrixAuthorizationStrategy/config.jelly b/core/src/main/resources/hudson/security/GlobalMatrixAuthorizationStrategy/config.jelly index 4f0592fe04..4b8f917eac 100644 --- a/core/src/main/resources/hudson/security/GlobalMatrixAuthorizationStrategy/config.jelly +++ b/core/src/main/resources/hudson/security/GlobalMatrixAuthorizationStrategy/config.jelly @@ -60,7 +60,7 @@ - + @@ -71,7 +71,7 @@ - + @@ -120,6 +120,10 @@ return false; } e = null; + }, + + "#${strategyid} TR.permission-row" : function(e) { + FormChecker.delayedCheck("${rootURL}/descriptor/${descriptor.clazz.name}/checkName?value="+encode(e.getAttribute("name")),"GET",e.firstChild); } }); diff --git a/core/src/main/resources/lib/layout/layout.jelly b/core/src/main/resources/lib/layout/layout.jelly index 255fb00e45..18dcafbc7c 100644 --- a/core/src/main/resources/lib/layout/layout.jelly +++ b/core/src/main/resources/lib/layout/layout.jelly @@ -30,6 +30,7 @@ + diff --git a/war/resources/scripts/hudson-behavior.js b/war/resources/scripts/hudson-behavior.js index 7f81df8d02..5af68a2925 100644 --- a/war/resources/scripts/hudson-behavior.js +++ b/war/resources/scripts/hudson-behavior.js @@ -22,7 +22,19 @@ var FormChecker = { inProgress : false, + /** + * Schedules a form field check. Executions are serialized to reduce the bandwidth impact. + * + * @param url + * Remote doXYZ URL that performs the check. Query string should include the field value. + * @param method + * HTTP method. GET or POST. I haven't confirmed specifics, but some browsers seem to cache GET requests. + * @param target + * HTML element whose innerHTML will be overwritten when the check is completed. + */ delayedCheck : function(url, method, target) { + if(url==null || method==null || target==null) + return; // don't know whether we should throw an exception or ignore this. some broken plugins have illegal parameters this.queue.push({url:url, method:method, target:target}); this.schedule(); }, @@ -41,8 +53,6 @@ var FormChecker = { if (this.queue.length == 0) return; var next = this.queue.shift(); - this.inProgress = true; - this.sendRequest(next.url, { method : next.method, onComplete : function(x) { @@ -51,6 +61,7 @@ var FormChecker = { FormChecker.schedule(); } }); + this.inProgress = true; } } -- GitLab