import javax.annotation.CheckForNull;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.sonar.api.Startable;
import org.sonar.api.config.Settings;
import org.sonar.api.server.authentication.Display;
import org.sonar.api.server.authentication.IdentityProvider;
import org.sonar.api.server.authentication.UnauthorizedException;
import org.sonar.api.server.authentication.UserIdentity;
import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.BadRequestException;
import static org.apache.commons.lang.time.DateUtils.addMinutes;
import static org.sonar.server.user.UserUpdater.SQ_AUTHORITY;
-public class SsoAuthenticator {
+public class SsoAuthenticator implements Startable {
+
+ private static final Logger LOG = Loggers.get(SsoAuthenticator.class);
private static final Splitter COMA_SPLITTER = Splitter.on(",").trimResults().omitEmptyStrings();
private static final String LAST_REFRESH_TIME_TOKEN_PARAM = "ssoLastRefreshTime";
- private static final Map<String, String> DEFAULT_VALUES_BY_PARAMETERS = ImmutableMap.of(
+ private static final Map<String, String> DEFAULT_VALUES_BY_SETTING_KEYS = ImmutableMap.of(
LOGIN_HEADER_PARAM, LOGIN_HEADER_DEFAULT_VALUE,
NAME_HEADER_PARAM, NAME_HEADER_DEFAULT_VALUE,
EMAIL_HEADER_PARAM, EMAIL_HEADER_DEFAULT_VALUE,
private final UserIdentityAuthenticator userIdentityAuthenticator;
private final JwtHttpHandler jwtHttpHandler;
+ private boolean enabled = false;
+ private Map<String, String> settingsByKey = new HashMap<>();
+
public SsoAuthenticator(System2 system2, Settings settings, UserIdentityAuthenticator userIdentityAuthenticator, JwtHttpHandler jwtHttpHandler) {
this.system2 = system2;
this.settings = settings;
this.jwtHttpHandler = jwtHttpHandler;
}
+ @Override
+ public void start() {
+ if (settings.getBoolean(ENABLE_PARAM)) {
+ LOG.info("SSO Authentication enabled");
+ enabled = true;
+ DEFAULT_VALUES_BY_SETTING_KEYS.entrySet()
+ .forEach(entry -> settingsByKey.put(entry.getKey(), defaultIfBlank(settings.getString(entry.getKey()), DEFAULT_VALUES_BY_SETTING_KEYS.get(entry.getKey()))));
+ }
+ }
+
+ @Override
+ public void stop() {
+ // Nothing to do
+ }
+
public Optional<UserDto> authenticate(HttpServletRequest request, HttpServletResponse response) {
try {
return doAuthenticate(request, response);
}
private Optional<UserDto> doAuthenticate(HttpServletRequest request, HttpServletResponse response) {
- if (!settings.getBoolean(ENABLE_PARAM)) {
+ if (!enabled) {
return Optional.empty();
}
Map<String, String> headerValuesByNames = getHeaders(request);
return Optional.empty();
}
Date now = new Date(system2.now());
- int refreshIntervalInMinutes = Integer.parseInt(getSettingValue(REFRESH_INTERVAL_PARAM));
+ int refreshIntervalInMinutes = Integer.parseInt(settingsByKey.get(REFRESH_INTERVAL_PARAM));
Long lastFreshTime = (Long) token.get().getProperties().get(LAST_REFRESH_TIME_TOKEN_PARAM);
if (lastFreshTime == null || now.after(addMinutes(new Date(lastFreshTime), refreshIntervalInMinutes))) {
return Optional.empty();
@CheckForNull
private String getHeaderValue(Map<String, String> headerValuesByNames, String settingKey) {
- return headerValuesByNames.get(getSettingValue(settingKey).toLowerCase(Locale.ENGLISH));
+ return headerValuesByNames.get(settingsByKey.get(settingKey).toLowerCase(Locale.ENGLISH));
}
private static Map<String, String> getHeaders(HttpServletRequest request) {
}
private boolean hasHeader(Map<String, String> headerValuesByNames, String settingKey) {
- return headerValuesByNames.keySet().contains(getSettingValue(settingKey).toLowerCase(Locale.ENGLISH));
- }
-
- private String getSettingValue(String settingKey) {
- return defaultIfBlank(settings.getString(settingKey), DEFAULT_VALUES_BY_PARAMETERS.get(settingKey));
+ return headerValuesByNames.keySet().contains(settingsByKey.get(settingKey).toLowerCase(Locale.ENGLISH));
}
private static class SsoIdentityProvider implements IdentityProvider {
@Test
public void create_user_when_authenticating_new_user() throws Exception {
- enableSso();
+ startWithSso();
setNotUserInToken();
HttpServletRequest request = createRequest(DEFAULT_LOGIN, DEFAULT_NAME, DEFAULT_EMAIL, GROUPS);
@Test
public void use_login_when_name_is_not_provided() throws Exception {
- enableSso();
+ startWithSso();
setNotUserInToken();
underTest.authenticate(createRequest(DEFAULT_LOGIN, null, null, null), response);
@Test
public void update_user_when_authenticating_exiting_user() throws Exception {
- enableSso();
+ startWithSso();
setNotUserInToken();
insertUser(newUserDto().setLogin(DEFAULT_LOGIN).setName("old name").setEmail("old email"), group1);
// Name, email and groups are different
@Test
public void remove_groups_when_group_headers_is_empty() throws Exception {
- enableSso();
+ startWithSso();
setNotUserInToken();
insertUser(DEFAULT_USER, group1);
@Test
public void remove_groups_when_group_headers_is_null() throws Exception {
- enableSso();
+ startWithSso();
setNotUserInToken();
insertUser(DEFAULT_USER, group1);
Map<String, String> headerValuesByName = new HashMap<>();
@Test
public void does_not_update_groups_when_no_group_headers() throws Exception {
- enableSso();
+ startWithSso();
setNotUserInToken();
insertUser(DEFAULT_USER, group1);
@Test
public void does_not_update_user_when_user_is_in_token_and_refresh_time_is_close() throws Exception {
- enableSso();
+ startWithSso();
UserDto user = insertUser(DEFAULT_USER, group1);
setUserInToken(user, CLOSE_REFRESH_TIME);
HttpServletRequest request = createRequest(DEFAULT_LOGIN, "new name", "new email", GROUP2);
@Test
public void update_user_when_user_in_token_but_refresh_time_is_old() throws Exception {
- enableSso();
+ startWithSso();
UserDto user = insertUser(DEFAULT_USER, group1);
// Refresh time was updated 6 minutes ago => more than 5 minutes
setUserInToken(user, NOW - 6 * 60 * 1000L);
@Test
public void update_user_when_user_in_token_but_no_refresh_time() throws Exception {
- enableSso();
+ startWithSso();
UserDto user = insertUser(DEFAULT_USER, group1);
setUserInToken(user, null);
HttpServletRequest request = createRequest(DEFAULT_LOGIN, "new name", "new email", GROUP2);
@Test
public void use_refresh_time_from_settings() throws Exception {
- enableSso();
settings.setProperty("sonar.sso.refreshIntervalInMinutes", "10");
+ startWithSso();
UserDto user = insertUser(DEFAULT_USER, group1);
// Refresh time was updated 6 minutes ago => less than 10 minutes ago so not updated
setUserInToken(user, NOW - 6 * 60 * 1000L);
@Test
public void update_user_when_login_from_token_is_different_than_login_from_request() throws Exception {
- enableSso();
+ startWithSso();
insertUser(DEFAULT_USER, group1);
setUserInToken(DEFAULT_USER, CLOSE_REFRESH_TIME);
HttpServletRequest request = createRequest("AnotherLogin", "Another name", "Another email", GROUP2);
@Test
public void use_headers_from_settings() throws Exception {
- enableSso();
- setNotUserInToken();
settings.setProperty("sonar.sso.loginHeader", "head-login");
settings.setProperty("sonar.sso.nameHeader", "head-name");
settings.setProperty("sonar.sso.emailHeader", "head-email");
settings.setProperty("sonar.sso.groupsHeader", "head-groups");
+ startWithSso();
+ setNotUserInToken();
HttpServletRequest request = createRequest(ImmutableMap.of("head-login", DEFAULT_LOGIN, "head-name", DEFAULT_NAME, "head-email", DEFAULT_EMAIL, "head-groups", GROUPS));
underTest.authenticate(request, response);
@Test
public void detect_group_header_even_with_wrong_case() throws Exception {
- enableSso();
- setNotUserInToken();
settings.setProperty("sonar.sso.loginHeader", "login");
settings.setProperty("sonar.sso.nameHeader", "name");
settings.setProperty("sonar.sso.emailHeader", "email");
settings.setProperty("sonar.sso.groupsHeader", "Groups");
+ startWithSso();
+ setNotUserInToken();
HttpServletRequest request = createRequest(ImmutableMap.of("login", DEFAULT_LOGIN, "name", DEFAULT_NAME, "email", DEFAULT_EMAIL, "groups", GROUPS));
underTest.authenticate(request, response);
@Test
public void trim_groups() throws Exception {
- enableSso();
+ startWithSso();
setNotUserInToken();
HttpServletRequest request = createRequest(DEFAULT_LOGIN, null, null, " dev , admin ");
@Test
public void does_not_authenticate_when_no_header() throws Exception {
- enableSso();
+ startWithSso();
setNotUserInToken();
underTest.authenticate(createRequest(Collections.emptyMap()), response);
@Test
public void does_not_authenticate_when_not_enabled() throws Exception {
- settings.setProperty("sonar.sso.enable", false);
+ startWithoutSso();
underTest.authenticate(createRequest(DEFAULT_LOGIN, DEFAULT_NAME, DEFAULT_EMAIL, GROUPS), response);
@Test
public void throw_UnauthorizedException_when_BadRequestException_is_generated() throws Exception {
- enableSso();
+ startWithSso();
setNotUserInToken();
expectedException.expect(UnauthorizedException.class);
underTest.authenticate(createRequest("invalid login", DEFAULT_NAME, DEFAULT_EMAIL, GROUPS), response);
}
- private void enableSso() {
+ private void startWithSso() {
settings.setProperty("sonar.sso.enable", true);
+ underTest.start();
+ }
+
+ private void startWithoutSso() {
+ settings.setProperty("sonar.sso.enable", false);
+ underTest.start();
}
private void setUserInToken(UserDto user, @Nullable Long lastRefreshTime) {