/*************************GO-LICENSE-START********************************* * Copyright 2014 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. *************************GO-LICENSE-END***********************************/ package com.thoughtworks.go.server.security.providers; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import com.thoughtworks.go.server.security.AuthorityGranter; import com.thoughtworks.go.server.security.userdetail.GoUserPrinciple; import com.thoughtworks.go.server.service.GoConfigService; import com.thoughtworks.go.server.service.SecurityService; import com.thoughtworks.go.server.service.UserService; import com.thoughtworks.go.util.StringUtil; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.AuthenticationException; import org.springframework.security.BadCredentialsException; import org.springframework.security.providers.UsernamePasswordAuthenticationToken; import org.springframework.security.providers.dao.AbstractUserDetailsAuthenticationProvider; import org.springframework.security.userdetails.User; import org.springframework.security.userdetails.UserDetails; import org.springframework.security.userdetails.UsernameNotFoundException; import org.springframework.security.userdetails.memory.UserMap; import org.springframework.security.userdetails.memory.UserMapEditor; public class FileAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { private final GoConfigService goConfigService; private final AuthorityGranter authorityGranter; private final UserService userService; private final SecurityService securityService; @Autowired public FileAuthenticationProvider(GoConfigService goConfigService, AuthorityGranter authorityGranter, UserService userService, SecurityService securityService) { this.goConfigService = goConfigService; this.authorityGranter = authorityGranter; this.userService = userService; this.securityService = securityService; } protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { final String plainTextPassword = (String) authentication.getCredentials(); if (!userDetails.getPassword().equals(StringUtil.sha1Digest(plainTextPassword.getBytes()))) { throw new BadCredentialsException( messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credential")); } } protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { final String passwordFilePath = goConfigService.security().passwordFileConfig().path(); try { UserMap userMap = UserMapEditor.addUsersFromProperties(new UserMap(), addDummyRoleToPropertiesIfRequired(stripShaFromPasswordsIfRequired(loadPasswordFile(passwordFilePath)))); final UserDetails details = userMap.getUser(username); return userStrippedOfAnyAuthoritiesSpecifiedInFile(username, details); } catch (IOException e) { throw new UsernameNotFoundException( "Trying to authenticate user " + username + " but could not open file: " + passwordFilePath); } } private Properties loadPasswordFile(String passwordFilePath) throws IOException { final Properties properties = new Properties(); InputStream inputStream = null; try { inputStream = new FileInputStream(passwordFilePath); properties.load(inputStream); } finally { IOUtils.closeQuietly(inputStream); } return properties; } private User userStrippedOfAnyAuthoritiesSpecifiedInFile(String username, UserDetails details) { com.thoughtworks.go.domain.User user = userService.findUserByName(details.getUsername()); String displayName = username; if (user != null && !user.getDisplayName().isEmpty()) { displayName = user.getDisplayName(); } return new GoUserPrinciple(details.getUsername(), displayName, details.getPassword(), details.isEnabled(), details.isAccountNonExpired(), details.isCredentialsNonExpired(), details.isAccountNonLocked(), authorityGranter.authorities(username)); } private Properties addDummyRoleToPropertiesIfRequired(Properties properties) { for (Object key : properties.keySet()) { String value = properties.getProperty(String.valueOf(key)); if (!value.contains(",")) { properties.setProperty(String.valueOf(key), value + ",ROLE_USER"); } } return properties; } private Properties stripShaFromPasswordsIfRequired(Properties properties) { for (Object key : properties.keySet()) { String value = properties.getProperty(String.valueOf(key)); if (value.startsWith("{SHA}")) { properties.setProperty(String.valueOf(key), value.substring(5)); } } return properties; } @Override public boolean supports(Class authentication) { return isPasswordFileConfigured() && super.supports(authentication); } private boolean isPasswordFileConfigured() { return goConfigService.security().passwordFileConfig().isEnabled(); } }