/*
* Copyright 2017 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.thoughtworks.go.server.security.providers;
import com.thoughtworks.go.config.CaseInsensitiveString;
import com.thoughtworks.go.config.PluginRoleConfig;
import com.thoughtworks.go.config.SecurityAuthConfig;
import com.thoughtworks.go.config.SecurityConfig;
import com.thoughtworks.go.domain.config.ConfigurationProperty;
import com.thoughtworks.go.plugin.access.authentication.models.User;
import com.thoughtworks.go.plugin.access.authorization.AuthorizationExtension;
import com.thoughtworks.go.plugin.access.authorization.models.AuthenticationResponse;
import com.thoughtworks.go.server.security.AuthorityGranter;
import com.thoughtworks.go.server.security.GoAuthority;
import com.thoughtworks.go.server.security.tokens.PreAuthenticatedAuthenticationToken;
import com.thoughtworks.go.server.security.userdetail.GoUserPrinciple;
import com.thoughtworks.go.server.service.GoConfigService;
import com.thoughtworks.go.server.service.PluginRoleService;
import com.thoughtworks.go.server.service.UserService;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.springframework.security.Authentication;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import java.util.*;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
public class PreAuthenticatedAuthenticationProviderTest {
private AuthorizationExtension authorizationExtension;
private String pluginId;
private AuthorityGranter authorityGranter;
private UserService userService;
private PluginRoleService pluginRoleService;
private PreAuthenticatedAuthenticationProvider authenticationProvider;
private User user;
private GrantedAuthority[] authorities;
@Rule
public ExpectedException thrown = ExpectedException.none();
private GoConfigService goConfigService;
private SecurityConfig securityConfig;
@Before
public void setUp() throws Exception {
pluginId = "github.oauth";
user = new User("username", "displayname", "emailId");
authorities = new GrantedAuthority[]{GoAuthority.ROLE_USER.asAuthority()};
authorizationExtension = mock(AuthorizationExtension.class);
authorityGranter = mock(AuthorityGranter.class);
userService = mock(UserService.class);
pluginRoleService = mock(PluginRoleService.class);
goConfigService = mock(GoConfigService.class);
authenticationProvider = new PreAuthenticatedAuthenticationProvider(authorizationExtension, pluginRoleService, userService, authorityGranter, goConfigService);
AuthenticationResponse authenticationResponse = new AuthenticationResponse(user, Arrays.asList("admin"));
securityConfig = new SecurityConfig();
stub(goConfigService.security()).toReturn(securityConfig);
stub(authorizationExtension.authenticateUser(any(String.class), any(Map.class), any(List.class), any(List.class))).toReturn(authenticationResponse);
stub(authorityGranter.authorities(anyString())).toReturn(authorities);
}
@Test
public void authenticate_shouldAuthenticateUserAgainstTheSpecifiedPlugin() throws Exception {
Map<String, String> credentials = Collections.singletonMap("access_token", "some_token");
SecurityAuthConfig githubConfig = new SecurityAuthConfig("github", pluginId);
PluginRoleConfig adminRole = new PluginRoleConfig("admin", "github", new ConfigurationProperty());
securityConfig.securityAuthConfigs().add(githubConfig);
securityConfig.addRole(adminRole);
PreAuthenticatedAuthenticationToken authenticationToken = new PreAuthenticatedAuthenticationToken(null, credentials, pluginId);
authenticationProvider.authenticate(authenticationToken);
verify(authorizationExtension).authenticateUser(pluginId, credentials, Collections.singletonList(githubConfig), Collections.singletonList(adminRole));
}
@Test
public void authenticate_shouldCreateUserIfDoesNotExist() {
Map<String, String> credentials = Collections.singletonMap("access_token", "some_token");
PreAuthenticatedAuthenticationToken authenticationToken = new PreAuthenticatedAuthenticationToken(null, credentials, pluginId);
authenticationProvider.authenticate(authenticationToken);
ArgumentCaptor<com.thoughtworks.go.domain.User> argumentCaptor = ArgumentCaptor.forClass(com.thoughtworks.go.domain.User.class);
verify(userService).addUserIfDoesNotExist(argumentCaptor.capture());
com.thoughtworks.go.domain.User user = argumentCaptor.getValue();
assertThat(user.getName(), is(this.user.getUsername()));
assertThat(user.getDisplayName(), is(this.user.getDisplayName()));
assertThat(user.getEmail(), is(this.user.getEmailId()));
}
@Test
public void authenticate_shouldAssignRolesToUser() throws Exception {
Map<String, String> credentials = Collections.singletonMap("access_token", "some_token");
PreAuthenticatedAuthenticationToken authenticationToken = new PreAuthenticatedAuthenticationToken(null, credentials, pluginId);
authenticationProvider.authenticate(authenticationToken);
verify(pluginRoleService).updatePluginRoles(pluginId, user.getUsername(), CaseInsensitiveString.caseInsensitiveStrings("admin"));
}
@Test
public void authenticate_shouldReturnAuthenticationTokenOnSuccessfulAuthorization() throws Exception {
Map<String, String> credentials = Collections.singletonMap("access_token", "some_token");
PreAuthenticatedAuthenticationToken authenticationToken = new PreAuthenticatedAuthenticationToken(null, credentials, pluginId);
PreAuthenticatedAuthenticationToken authenticate = (PreAuthenticatedAuthenticationToken) authenticationProvider.authenticate(authenticationToken);
assertThat(authenticate.getCredentials(), is(credentials));
assertThat(authenticate.getPluginId(), is(pluginId));
assertThat(authenticate.getAuthorities(), is(authorities));
assertThat(authenticate.isAuthenticated(), is(true));
}
@Test
public void authenticate_shouldReturnAuthTokenWithUserDetails() throws Exception {
Map<String, String> credentials = Collections.singletonMap("access_token", "some_token");
PreAuthenticatedAuthenticationToken authenticationToken = new PreAuthenticatedAuthenticationToken(null, credentials, pluginId);
PreAuthenticatedAuthenticationToken authenticate = (PreAuthenticatedAuthenticationToken) authenticationProvider.authenticate(authenticationToken);
GoUserPrinciple principal = (GoUserPrinciple) authenticate.getPrincipal();
assertThat(principal.getDisplayName(), is(user.getDisplayName()));
assertThat(principal.getUsername(), is(user.getUsername()));
assertThat(principal.getAuthorities(), is(authorities));
}
@Test
public void authenticate_shouldEnsureUserDetailsInAuthTokenHasDisplayName() {
Map<String, String> credentials = Collections.singletonMap("access_token", "some_token");
PreAuthenticatedAuthenticationToken authenticationToken = new PreAuthenticatedAuthenticationToken(null, credentials, pluginId);
AuthenticationResponse authenticationResponse = new AuthenticationResponse(new User("username", null, "email"), Arrays.asList("admin"));
when(authorizationExtension.authenticateUser(any(String.class), any(Map.class), any(List.class), any(List.class))).thenReturn(authenticationResponse);
PreAuthenticatedAuthenticationToken authenticate = (PreAuthenticatedAuthenticationToken) authenticationProvider.authenticate(authenticationToken);
GoUserPrinciple principal = (GoUserPrinciple) authenticate.getPrincipal();
assertThat(principal.getDisplayName(), is(authenticationResponse.getUser().getUsername()));
}
@Test
public void authenticate_shouldSupportAuthenticationForPreAuthenticatedAuthenticationTokenOnly() throws Exception {
Authentication authenticate = authenticationProvider.authenticate(new UsernamePasswordAuthenticationToken("p", "c"));
assertNull(authenticate);
verifyZeroInteractions(authorizationExtension);
}
@Test
public void authenticate_shouldErrorOutInAbsenceOfCredentials() throws Exception {
thrown.expect(BadCredentialsException.class);
thrown.expectMessage("No pre-authenticated credentials found in request.");
authenticationProvider.authenticate(new PreAuthenticatedAuthenticationToken(null, null, null));
}
@Test
public void authenticate_shouldHandleFailedAuthentication() throws Exception {
PreAuthenticatedAuthenticationToken authenticationToken = new PreAuthenticatedAuthenticationToken(null, Collections.singletonMap("access_token", "invalid_token"), pluginId);
AuthenticationResponse authenticationResponse = new AuthenticationResponse(null, null);
when(authorizationExtension.authenticateUser(any(String.class), any(Map.class), any(List.class), any(List.class))).thenReturn(authenticationResponse);
PreAuthenticatedAuthenticationToken authenticate = (PreAuthenticatedAuthenticationToken) authenticationProvider.authenticate(authenticationToken);
assertNull(authenticate);
}
}