import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.authentication.event.AuthenticationException;
-import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.user.ServerUserSession;
import org.sonar.server.user.ThreadLocalUserSession;
import static org.sonar.api.web.ServletFilter.UrlPattern;
import static org.sonar.api.web.ServletFilter.UrlPattern.Builder.staticResourcePatterns;
import static org.sonar.server.authentication.AuthenticationError.handleAuthenticationError;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Method;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
import static org.sonar.server.authentication.ws.LoginAction.AUTH_LOGIN_URL;
import static org.sonar.server.authentication.ws.ValidateAction.AUTH_VALIDATE_URL;
import static org.sonar.server.user.ServerUserSession.createForAnonymous;
response.setStatus(HTTP_UNAUTHORIZED);
return false;
}
- handleAuthenticationError(e, response);
- return false;
- } catch (UnauthorizedException e) {
- response.setStatus(HTTP_UNAUTHORIZED);
- if (isWsUrl(path)) {
+ if (isNotLocalOrJwt(e.getSource())) {
+ // redirect to Unauthorized error page
+ handleAuthenticationError(e, response);
return false;
}
- // WS should stop here. Rails page should continue in order to deal with redirection
+ // Rails will redirect to login page
return true;
}
}
- private static boolean shouldContinueFilterOnError(String path) {
- if (isWsUrl(path)) {
- return false;
- }
- // WS should stop here. Rails page should continue in order to deal with redirection
- return true;
+ private static boolean isNotLocalOrJwt(Source source) {
+ AuthenticationEvent.Provider provider = source.getProvider();
+ return provider != AuthenticationEvent.Provider.LOCAL && provider != AuthenticationEvent.Provider.JWT;
}
private void setUserSession(HttpServletRequest request, HttpServletResponse response) {
request.setAttribute(ACCESS_LOG_LOGIN, session.getLogin());
} else {
if (settings.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY)) {
- throw new UnauthorizedException("User must be authenticated");
+ throw AuthenticationException.newBuilder()
+ .setSource(Source.local(Method.BASIC))
+ .setMessage("User must be authenticated")
+ .build();
}
threadLocalSession.set(createForAnonymous(dbClient));
request.setAttribute(ACCESS_LOG_LOGIN, "-");
.setAllowsUsersToSignUp(false);
Source source = Source.realm(Method.FORM, identityProvider.getName());
- thrown.expect(authenticationException().from(source).withLogin(USER_IDENTITY.getLogin()).andNoPublicMessage());
- thrown.expectMessage("'github' users are not allowed to sign up");
+ thrown.expect(authenticationException().from(source).withLogin(USER_IDENTITY.getLogin()).andPublicMessage("'github' users are not allowed to sign up"));
+ thrown.expectMessage("user signup disabled for provider 'github'");
underTest.authenticate(USER_IDENTITY, identityProvider, source);
}
.setEmail("john@email.com"));
Source source = Source.realm(Method.FORM, IDENTITY_PROVIDER.getName());
- thrown.expect(authenticationException().from(source).withLogin(USER_IDENTITY.getLogin()).andNoPublicMessage());
- thrown.expectMessage("You can't sign up because email 'john@email.com' is already used by an existing user. " +
- "This means that you probably already registered with another account.");
+ thrown.expect(authenticationException().from(source)
+ .withLogin(USER_IDENTITY.getLogin())
+ .andPublicMessage("You can't sign up because email 'john@email.com' is already used by an existing user. " +
+ "This means that you probably already registered with another account."));
+ thrown.expectMessage("email 'john@email.com' is already used");
underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, source);
}
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.sonar.api.config.MapSettings;
import org.sonar.api.config.Settings;
import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.event.AuthenticationEvent;
-import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.authentication.event.AuthenticationException;
import org.sonar.server.user.ServerUserSession;
import org.sonar.server.user.ThreadLocalUserSession;
import org.sonar.server.user.UserSession;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.sonar.db.user.UserTesting.newUserDto;
+import static org.sonar.server.authentication.event.AuthenticationEvent.*;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
public class UserSessionInitializerTest {
private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
private BasicAuthenticator basicAuthenticator = mock(BasicAuthenticator.class);
private SsoAuthenticator ssoAuthenticator = mock(SsoAuthenticator.class);
+ private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class);
private Settings settings = new MapSettings();
private UserDto user = newUserDto();
- private UserSessionInitializer underTest = new UserSessionInitializer(dbClient, settings, jwtHttpHandler, basicAuthenticator, ssoAuthenticator, userSession, mock(AuthenticationEvent.class));
+ private UserSessionInitializer underTest = new UserSessionInitializer(dbClient, settings, jwtHttpHandler, basicAuthenticator,
+ ssoAuthenticator, userSession, authenticationEvent);
@Before
public void setUp() throws Exception {
@Test
public void return_code_401_when_invalid_token_exception() throws Exception {
when(ssoAuthenticator.authenticate(request, response)).thenReturn(Optional.empty());
- doThrow(new UnauthorizedException("invalid token")).when(jwtHttpHandler).validateToken(request, response);
+ AuthenticationException authenticationException = AuthenticationException.newBuilder().setSource(Source.jwt()).setMessage("Token id hasn't been found").build();
+ doThrow(authenticationException).when(jwtHttpHandler).validateToken(request, response);
assertThat(underTest.initUserSession(request, response)).isTrue();
- verify(response).setStatus(401);
- verifyZeroInteractions(userSession);
+ verify(authenticationEvent).failure(request, authenticationException);
+ verifyZeroInteractions(response, userSession);
}
@Test
public void return_code_401_when_not_authenticated_and_with_force_authentication() throws Exception {
+ ArgumentCaptor<AuthenticationException> exceptionArgumentCaptor = ArgumentCaptor.forClass(AuthenticationException.class);
when(userSession.isLoggedIn()).thenReturn(false);
when(basicAuthenticator.authenticate(request)).thenReturn(Optional.empty());
when(ssoAuthenticator.authenticate(request, response)).thenReturn(Optional.empty());
assertThat(underTest.initUserSession(request, response)).isTrue();
- verify(response).setStatus(401);
+ verifyZeroInteractions(response);
+ verify(authenticationEvent).failure(eq(request), exceptionArgumentCaptor.capture());
verifyZeroInteractions(userSession);
+ AuthenticationException authenticationException = exceptionArgumentCaptor.getValue();
+ assertThat(authenticationException.getSource()).isEqualTo(Source.local(Method.BASIC));
+ assertThat(authenticationException.getLogin()).isNull();
+ assertThat(authenticationException.getMessage()).isEqualTo("User must be authenticated");
+ assertThat(authenticationException.getPublicMessage()).isNull();
}
@Test
public void return_401_and_stop_on_ws() throws Exception {
when(request.getRequestURI()).thenReturn("/api/issues");
when(ssoAuthenticator.authenticate(request, response)).thenReturn(Optional.empty());
- doThrow(new UnauthorizedException("invalid token")).when(jwtHttpHandler).validateToken(request, response);
+ AuthenticationException authenticationException = AuthenticationException.newBuilder().setSource(Source.jwt()).setMessage("Token id hasn't been found").build();
+ doThrow(authenticationException).when(jwtHttpHandler).validateToken(request, response);
assertThat(underTest.initUserSession(request, response)).isFalse();
verify(response).setStatus(401);
+ verify(authenticationEvent).failure(request, authenticationException);
verifyZeroInteractions(userSession);
}
public void return_401_and_stop_on_batch_ws() throws Exception {
when(request.getRequestURI()).thenReturn("/batch/global");
when(ssoAuthenticator.authenticate(request, response)).thenReturn(Optional.empty());
- doThrow(new UnauthorizedException("invalid token")).when(jwtHttpHandler).validateToken(request, response);
+ doThrow(AuthenticationException.newBuilder().setSource(Source.jwt()).setMessage("Token id hasn't been found").build())
+ .when(jwtHttpHandler).validateToken(request, response);
assertThat(underTest.initUserSession(request, response)).isFalse();