提交 61ed2dbd 编写于 作者: J Jesse Glick

Merge pull request #577 from jglick/User.authorities

Display authorities at /user/* for convenience.
......@@ -55,6 +55,9 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class=rfe>
Display authorities at <code>/user/*</code> for convenience.
(<a href="https://github.com/jenkinsci/jenkins/pull/577">pull 577</a>)
<li class=bug>
Slow rendering of view pages in large installations due to eager check whether the “People” link would show anything.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-16244">issue 16244</a>)
......
......@@ -40,11 +40,12 @@ import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.springframework.dao.DataAccessException;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
......@@ -52,7 +53,6 @@ import org.kohsuke.stapler.export.ExportedBean;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.annotation.Nullable;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
......@@ -254,11 +254,14 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
try {
UserDetails u = Jenkins.getInstance().getSecurityRealm().loadUserByUsername(id);
return new UsernamePasswordAuthenticationToken(u.getUsername(), "", u.getAuthorities());
} catch (AuthenticationException e) {
// TODO: use the stored GrantedAuthorities
return new UsernamePasswordAuthenticationToken(id, "",
new GrantedAuthority[]{SecurityRealm.AUTHENTICATED_AUTHORITY});
} catch (UsernameNotFoundException e) {
// ignore
} catch (DataAccessException e) {
// ignore
}
// TODO: use the stored GrantedAuthorities
return new UsernamePasswordAuthenticationToken(id, "",
new GrantedAuthority[]{SecurityRealm.AUTHENTICATED_AUTHORITY});
}
/**
......@@ -624,6 +627,30 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
&& new File(getRootDir(), id).exists();
}
/**
* Checks for authorities (groups) associated with this user.
* If the caller lacks {@link Jenkins#ADMINISTER}, or any problems arise, returns an empty list.
* {@link SecurityRealm#AUTHENTICATED_AUTHORITY} and the username, if present, are omitted.
* @since 1.498
* @return a possibly empty list
*/
public @Nonnull List<String> getAuthorities() {
if (!Jenkins.getInstance().hasPermission(Jenkins.ADMINISTER)) {
return Collections.emptyList();
}
List<String> r = new ArrayList<String>();
for (GrantedAuthority a : impersonate().getAuthorities()) {
if (a.equals(SecurityRealm.AUTHENTICATED_AUTHORITY)) {
continue;
}
String n = a.getAuthority();
if (n != null && !n.equals(id)) {
r.add(n);
}
}
return r;
}
public Descriptor getDescriptorByName(String className) {
return Jenkins.getInstance().getDescriptorByName(className);
}
......
......@@ -38,6 +38,15 @@ THE SOFTWARE.
<j:forEach var="p" items="${it.allProperties}">
<st:include page="summary.jelly" from="${p}" optional="true" it="${p}" />
</j:forEach>
<j:set var="authorities" value="${it.authorities}"/>
<j:if test="${!authorities.isEmpty()}">
<div>${%Groups}:</div>
<ul>
<j:forEach var="a" items="${authorities}">
<li>${a}</li>
</j:forEach>
</ul>
</j:if>
</l:main-panel>
</l:layout>
</j:jelly>
......@@ -135,7 +135,6 @@ import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import org.jvnet.hudson.test.recipes.Recipe;
import org.jvnet.hudson.test.rhino.JavaScriptDebugger;
import org.kohsuke.stapler.ClassDescriptor;
import org.kohsuke.stapler.DataBoundConstructor;
......@@ -183,11 +182,15 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
......@@ -201,6 +204,7 @@ import java.util.logging.Filter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import org.acegisecurity.GrantedAuthorityImpl;
import static org.hamcrest.Matchers.hasXPath;
import static org.hamcrest.Matchers.is;
......@@ -779,26 +783,62 @@ public class JenkinsRule implements TestRule, MethodRule, RootAction {
/**
* Creates a test {@link hudson.security.SecurityRealm} that recognizes username==password as valid.
*/
public SecurityRealm createDummySecurityRealm() {
return new AbstractPasswordBasedSecurityRealm() {
@Override
protected UserDetails authenticate(String username, String password) throws AuthenticationException {
if (username.equals(password))
return loadUserByUsername(username);
throw new BadCredentialsException(username);
public DummySecurityRealm createDummySecurityRealm() {
return new DummySecurityRealm();
}
/** @see #createDummySecurityRealm */
public static class DummySecurityRealm extends AbstractPasswordBasedSecurityRealm {
private final Map<String,Set<String>> groupsByUser = new HashMap<String,Set<String>>();
DummySecurityRealm() {}
@Override
protected UserDetails authenticate(String username, String password) throws AuthenticationException {
if (username.equals(password))
return loadUserByUsername(username);
throw new BadCredentialsException(username);
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,
DataAccessException {
List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
auths.add(AUTHENTICATED_AUTHORITY);
Set<String> groups = groupsByUser.get(username);
if (groups != null) {
for (String g : groups) {
auths.add(new GrantedAuthorityImpl(g));
}
}
return new org.acegisecurity.userdetails.User(username,"",true,true,true,true, auths.toArray(new GrantedAuthority[auths.size()]));
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,
DataAccessException {
return new org.acegisecurity.userdetails.User(username,"",true,true,true,true,new GrantedAuthority[]{AUTHENTICATED_AUTHORITY});
@Override
public GroupDetails loadGroupByGroupname(final String groupname) throws UsernameNotFoundException, DataAccessException {
for (Set<String> groups : groupsByUser.values()) {
if (groups.contains(groupname)) {
return new GroupDetails() {
@Override
public String getName() {
return groupname;
}
};
}
}
throw new UsernameNotFoundException(groupname);
}
@Override
public GroupDetails loadGroupByGroupname(String groupname) throws UsernameNotFoundException, DataAccessException {
throw new UsernameNotFoundException(groupname);
/** Associate some groups with a username. */
public void addGroups(String username, String... groups) {
Set<String> gs = groupsByUser.get(username);
if (gs == null) {
groupsByUser.put(username, gs = new TreeSet<String>());
}
};
gs.addAll(Arrays.asList(groups));
}
}
/**
......
......@@ -26,9 +26,21 @@ package hudson.model;
import com.gargoylesoftware.htmlunit.WebAssert;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.jvnet.hudson.test.HudsonTestCase;
import hudson.security.GlobalMatrixAuthorizationStrategy;
import hudson.security.Permission;
import jenkins.model.Jenkins;
import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.JenkinsRule;
public class UserTestCase extends HudsonTestCase {
public class UserTest {
@Rule public JenkinsRule j = new JenkinsRule();
public static class UserPropertyImpl extends UserProperty implements Action {
......@@ -73,10 +85,8 @@ public class UserTestCase extends HudsonTestCase {
}
}
/**
* Asserts that bug# is fixed.
*/
public void testUserPropertySummaryAndActionAreShownInUserPage() throws Exception {
@Bug(2331)
@Test public void userPropertySummaryAndActionAreShownInUserPage() throws Exception {
UserProperty property = new UserPropertyImpl("NeedleInPage");
UserProperty.all().add(property.getDescriptor());
......@@ -84,7 +94,7 @@ public class UserTestCase extends HudsonTestCase {
User user = User.get("user-test-case");
user.addProperty(property);
HtmlPage page = new WebClient().goTo("user/user-test-case");
HtmlPage page = j.createWebClient().goTo("user/user-test-case");
WebAssert.assertTextPresentInElement(page, "NeedleInPage", "main-panel");
WebAssert.assertTextPresentInElement(page, ((Action) property).getDisplayName(), "side-panel");
......@@ -94,9 +104,37 @@ public class UserTestCase extends HudsonTestCase {
/**
* Asserts that the default user avatar can be fetched (ie no 404)
*/
public void testDefaultUserAvatarCanBeFetched() throws Exception {
@Bug(7494)
@Test public void defaultUserAvatarCanBeFetched() throws Exception {
User user = User.get("avatar-user", true);
HtmlPage page = new WebClient().goTo("user/" + user.getDisplayName());
assertAllImageLoadSuccessfully(page);
HtmlPage page = j.createWebClient().goTo("user/" + user.getDisplayName());
j.assertAllImageLoadSuccessfully(page);
}
@Test public void getAuthorities() throws Exception {
JenkinsRule.DummySecurityRealm realm = j.createDummySecurityRealm();
realm.addGroups("administrator", "admins");
realm.addGroups("alice", "users");
realm.addGroups("bob", "users", "lpadmin", "bob");
j.jenkins.setSecurityRealm(realm);
GlobalMatrixAuthorizationStrategy auth = new GlobalMatrixAuthorizationStrategy();
auth.add(Jenkins.ADMINISTER, "admins");
auth.add(Permission.READ, "users");
j.jenkins.setAuthorizationStrategy(auth);
SecurityContext seccon = SecurityContextHolder.getContext();
Authentication orig = seccon.getAuthentication();
try {
seccon.setAuthentication(User.get("administrator").impersonate());
assertEquals("[admins]", User.get("administrator").getAuthorities().toString());
assertEquals("[users]", User.get("alice").getAuthorities().toString());
assertEquals("[lpadmin, users]", User.get("bob").getAuthorities().toString());
assertEquals("[]", User.get("MasterOfXaos").getAuthorities().toString());
seccon.setAuthentication(User.get("alice").impersonate());
assertEquals("[]", User.get("alice").getAuthorities().toString());
assertEquals("[]", User.get("bob").getAuthorities().toString());
} finally {
seccon.setAuthentication(orig);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册