Skip to content

Commit

Permalink
Merge pull request #24 from ikedam/feature/JENKINS-34279_UsernameNotF…
Browse files Browse the repository at this point in the history
…ound

[FIXED JENKINS-34279] Handle UsernameNotFoundException.
  • Loading branch information
ikedam committed May 28, 2016
2 parents 5a3088e + e4ff3a1 commit cf96cbb
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 3 deletions.
Expand Up @@ -44,6 +44,7 @@

import org.acegisecurity.Authentication;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.authorizeproject.AuthorizeProjectStrategy;
import org.jenkinsci.plugins.authorizeproject.AuthorizeProjectStrategyDescriptor;
Expand Down Expand Up @@ -108,8 +109,13 @@ public Authentication authenticate(Job<?, ?> project, Queue.Item item) {
// fallback to anonymous
return Jenkins.ANONYMOUS;
}
Authentication a = u.impersonate();
return a;
try {
Authentication a = u.impersonate();
return a;
} catch (UsernameNotFoundException e) {
LOGGER.log(Level.WARNING, String.format("Invalid User %s. Falls back to anonymous.", getUserid()), e);
return Jenkins.ANONYMOUS;
}
}

/**
Expand Down
Expand Up @@ -34,8 +34,11 @@
import hudson.model.Run;
import hudson.model.User;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.acegisecurity.Authentication;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.jenkinsci.plugins.authorizeproject.AuthorizeProjectStrategy;
import org.jenkinsci.plugins.authorizeproject.AuthorizeProjectStrategyDescriptor;
import org.kohsuke.stapler.DataBoundConstructor;
Expand All @@ -44,6 +47,7 @@
* Run builds as a user who triggered the build.
*/
public class TriggeringUsersAuthorizationStrategy extends AuthorizeProjectStrategy {
private static final Logger LOGGER = Logger.getLogger(TriggeringUsersAuthorizationStrategy.class.getName());
/**
*
*/
Expand All @@ -65,7 +69,12 @@ public Authentication authenticate(Job<?, ?> project, Queue.Item item) {
if (u == null) {
return Jenkins.ANONYMOUS;
}
return u.impersonate();
try {
return u.impersonate();
} catch (UsernameNotFoundException e) {
LOGGER.log(Level.WARNING, String.format("Invalid User %s. Falls back to anonymous.", cause.getUserId()), e);
return Jenkins.ANONYMOUS;
}
}
return null;
}
Expand Down
Expand Up @@ -29,6 +29,7 @@
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
Expand All @@ -52,6 +53,7 @@
import org.jenkinsci.plugins.authorizeproject.AuthorizeProjectProperty;
import org.jenkinsci.plugins.authorizeproject.testutil.AuthorizationCheckBuilder;
import org.jenkinsci.plugins.authorizeproject.testutil.AuthorizeProjectJenkinsRule;
import org.jenkinsci.plugins.authorizeproject.testutil.SecurityRealmWithUserFilter;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
Expand Down Expand Up @@ -425,6 +427,37 @@ public void testAuthenticate() throws Exception {
}
}

@Test
public void testUsernotFoundException() throws Exception {
j.jenkins.setSecurityRealm(new SecurityRealmWithUserFilter(
j.createDummySecurityRealm(),
Arrays.asList("validuser")
));

// Users should be created before the test.
User.get("validuser");
User.get("invaliduser");

FreeStyleProject p = j.createFreeStyleProject();
AuthorizationCheckBuilder checker = new AuthorizationCheckBuilder();
p.getBuildersList().add(checker);

p.removeProperty(AuthorizeProjectProperty.class);
p.addProperty(new AuthorizeProjectProperty(new SpecificUsersAuthorizationStrategy("validuser", false)));

j.assertBuildStatusSuccess(p.scheduleBuild2(0).get(1, TimeUnit.SECONDS));
assertEquals("validuser", checker.authentication.getName());

// In case of specifying an invalid user,
// falls back to anonymous.
// And the build should not be blocked.
p.removeProperty(AuthorizeProjectProperty.class);
p.addProperty(new AuthorizeProjectProperty(new SpecificUsersAuthorizationStrategy("invaliduser", false)));

j.assertBuildStatusSuccess(p.scheduleBuild2(0).get(1, TimeUnit.SECONDS));
assertEquals(Jenkins.ANONYMOUS, checker.authentication);
}

@Test
@LocalData
public void testLoadOnStart() throws Exception {
Expand Down
Expand Up @@ -25,15 +25,25 @@
package org.jenkinsci.plugins.authorizeproject.strategy;

import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import jenkins.model.Jenkins;
import hudson.model.FreeStyleBuild;
import hudson.model.Cause;
import hudson.model.FreeStyleProject;
import hudson.model.User;
import hudson.security.ACL;
import hudson.tasks.BuildTrigger;

import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.jenkinsci.plugins.authorizeproject.AuthorizeProjectProperty;
import org.jenkinsci.plugins.authorizeproject.testutil.AuthorizationCheckBuilder;
import org.jenkinsci.plugins.authorizeproject.testutil.AuthorizeProjectJenkinsRule;
import org.jenkinsci.plugins.authorizeproject.testutil.SecurityRealmWithUserFilter;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
Expand Down Expand Up @@ -149,4 +159,41 @@ public void testAuthenticateDownstream() throws Exception {
b.delete();
}
}

@Test
public void testUsernotFoundException() throws Exception {
j.jenkins.setSecurityRealm(new SecurityRealmWithUserFilter(
j.createDummySecurityRealm(),
Arrays.asList("validuser")
));

// Users should be created before the test.
User.get("validuser");
User.get("invaliduser");

FreeStyleProject p = j.createFreeStyleProject();
p.addProperty(new AuthorizeProjectProperty(new TriggeringUsersAuthorizationStrategy()));
AuthorizationCheckBuilder checker = new AuthorizationCheckBuilder();
p.getBuildersList().add(checker);

SecurityContext orig = ACL.impersonate(new UsernamePasswordAuthenticationToken("validuser", "validuser"));
try {
j.assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause()).get(1, TimeUnit.SECONDS));
} finally {
SecurityContextHolder.setContext(orig);
}
assertEquals("validuser", checker.authentication.getName());

// In case of specifying an invalid user,
// falls back to anonymous.
// And the build should not be blocked.
orig = ACL.impersonate(new UsernamePasswordAuthenticationToken("invaliduser", "invaliduser"));
try {
j.assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause()).get(1, TimeUnit.SECONDS));
} finally {
SecurityContextHolder.setContext(orig);
}
assertEquals(Jenkins.ANONYMOUS, checker.authentication);
}

}
@@ -0,0 +1,73 @@
/*
* The MIT License
*
* Copyright (c) 2016 IKEDA Yasuyuki
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* 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.
*/

package org.jenkinsci.plugins.authorizeproject.testutil;

import java.util.List;

import hudson.security.SecurityRealm;

import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UserDetailsService;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.jvnet.hudson.test.JenkinsRule;
import org.springframework.dao.DataAccessException;

/**
* Wraps other {@link SecurityRealm},
* and throws {@link UsernameNotFoundException} for unknown users.
*
* Expected to be used with {@link JenkinsRule#createDummySecurityRealm()}
*/
public class SecurityRealmWithUserFilter extends SecurityRealm {
private final SecurityRealm baseSecurityRealm;
private final List<String> validUserList;

public SecurityRealmWithUserFilter(SecurityRealm baseSecurityRealm, List<String> validUserList) {
this.baseSecurityRealm = baseSecurityRealm;
this.validUserList = validUserList;
}

@Override
public SecurityComponents createSecurityComponents() {
final SecurityComponents baseComponent = baseSecurityRealm.createSecurityComponents();
return new SecurityComponents(
baseComponent.manager,
new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException
{
if (!validUserList.contains(username)) {
throw new UsernameNotFoundException(
String.format("%s is not listed as valid username.", username)
);
}
return baseComponent.userDetails.loadUserByUsername(username);
}
},
baseComponent.rememberMe
);
}
}

0 comments on commit cf96cbb

Please sign in to comment.