<commit_before>package org.taverna.server.master.identity;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.PreDestroy;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.taverna.server.master.utils.CallTimeLogger.PerfLogged;

/**
 * A stripped down version of a
 * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider
 * DaoAuthenticationProvider}/
 * {@link org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider
 * AbstractUserDetailsAuthenticationProvider} that avoids much of the overhead
 * associated with that class.
 */
public class StrippedDownAuthProvider implements AuthenticationProvider {
	/**
	 * The plaintext password used to perform
	 * {@link PasswordEncoder#isPasswordValid(String, String, Object)} on when
	 * the user is not found to avoid SEC-2056.
	 */
	private static final String USER_NOT_FOUND_PASSWORD = "<PASSWORD>";

	/**
	 * The password used to perform
	 * {@link PasswordEncoder#isPasswordValid(String, String, Object)} on when
	 * the user is not found to avoid SEC-2056. This is necessary, because some
	 * {@link PasswordEncoder} implementations will short circuit if the
	 * password is not in a valid format.
	 */
	private String userNotFoundEncodedPassword;
	private UserDetailsService userDetailsService;
	private PasswordEncoder passwordEncoder;
	private Map<String, String> authCache = new HashMap<String, String>();
	protected final Log logger = LogFactory.getLog(getClass());

	@PerfLogged
	@Override
	public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {

		if (!(authentication instanceof UsernamePasswordAuthenticationToken))
			throw new IllegalArgumentException(
					"can only authenticate against username+password");
		UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;

		// Determine username
		String username = (auth.getPrincipal() == null) ? "NONE_PROVIDED"
				: auth.getName();

		UserDetails user;

		try {
			user = retrieveUser(username, auth);
			if (user == null)
				throw new IllegalStateException(
						"retrieveUser returned null - a violation of the interface contract");
		} catch (UsernameNotFoundException notFound) {
			if (logger.isDebugEnabled())
				logger.debug("User '" + username + "' not found", notFound);
			throw new BadCredentialsException("Bad credentials");
		}

		// Pre-auth
		if (!user.isAccountNonLocked())
			throw new LockedException("User account is locked");
		if (!user.isEnabled())
			throw new DisabledException("User account is disabled");
		if (!user.isAccountNonExpired())
			throw new AccountExpiredException("User account has expired");
		Object credentials = auth.getCredentials();
		if (credentials == null) {
			logger.debug("Authentication failed: no credentials provided");

			throw new BadCredentialsException("Bad credentials");
		}

		String providedPassword = credentials.toString();
		boolean matched = false;
		synchronized (authCache) {
			String pw = authCache.get(username);
			if (pw != null && providedPassword != null) {
				if (pw.equals(providedPassword))
					matched = true;
				else
					authCache.remove(username);
			}
		}
		// Auth
		if (!matched) {
			if (!passwordEncoder.matches(providedPassword, user.getPassword())) {
				logger.debug("Authentication failed: password does not match stored value");

				throw new BadCredentialsException("Bad credentials");
			}
			if (providedPassword != null)
				synchronized (authCache) {
					authCache.put(username, providedPassword);
				}
		}

		// Post-auth
		if (!user.isCredentialsNonExpired())
			throw new CredentialsExpiredException(<commit_msg>Make the cache a little less eager.<commit_after>package org.taverna.server.master.identity;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.PreDestroy;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.taverna.server.master.utils.CallTimeLogger.PerfLogged;

/**
 * A stripped down version of a
 * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider
 * DaoAuthenticationProvider}/
 * {@link org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider
 * AbstractUserDetailsAuthenticationProvider} that avoids much of the overhead
 * associated with that class.
 */
public class StrippedDownAuthProvider implements AuthenticationProvider {
	/**
	 * The plaintext password used to perform
	 * {@link PasswordEncoder#isPasswordValid(String, String, Object)} on when
	 * the user is not found to avoid SEC-2056.
	 */
	private static final String USER_NOT_FOUND_PASSWORD = "<PASSWORD>";

	/**
	 * The password used to perform
	 * {@link PasswordEncoder#isPasswordValid(String, String, Object)} on when
	 * the user is not found to avoid SEC-2056. This is necessary, because some
	 * {@link PasswordEncoder} implementations will short circuit if the
	 * password is not in a valid format.
	 */
	private String userNotFoundEncodedPassword;
	private UserDetailsService userDetailsService;
	private PasswordEncoder passwordEncoder;
	private Map<String, AuthCacheEntry> authCache = new HashMap<String, AuthCacheEntry>();
	protected final Log logger = LogFactory.getLog(getClass());

	private static class AuthCacheEntry {
		private String creds;
		private long timestamp;
		private static final long VALIDITY = 1000 * 60 * 20;
		AuthCacheEntry(String credentials) {
			creds = credentials;
			timestamp = System.currentTimeMillis();
		}
		boolean valid(String password) {
			return creds.equals(password) && timestamp+VALIDITY < System.currentTimeMillis();
		}
	}

	@PerfLogged
	@Override
	public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {

		if (!(authentication instanceof UsernamePasswordAuthenticationToken))
			throw new IllegalArgumentException(
					"can only authenticate against username+password");
		UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;

		// Determine username
		String username = (auth.getPrincipal() == null) ? "NONE_PROVIDED"
				: auth.getName();

		UserDetails user;

		try {
			user = retrieveUser(username, auth);
			if (user == null)
				throw new IllegalStateException(
						"retrieveUser returned null - a violation of the interface contract");
		} catch (UsernameNotFoundException notFound) {
			if (logger.isDebugEnabled())
				logger.debug("User '" + username + "' not found", notFound);
			throw new BadCredentialsException("Bad credentials");
		}

		// Pre-auth
		if (!user.isAccountNonLocked())
			throw new LockedException("User account is locked");
		if (!user.isEnabled())
			throw new DisabledException("User account is disabled");
		if (!user.isAccountNonExpired())
			throw new AccountExpiredException("User account has expired");
		Object credentials = auth.getCredentials();
		if (credentials == null) {
			logger.debug("Authentication failed: no credentials provided");

			throw new BadCredentialsException("Bad credentials");
		}

		String providedPassword = credentials.toString();
		boolean matched = false;
		synchronized (authCache) {
			AuthCacheEntry pw = authCache.get(username);
			if (pw != null && providedPassword != null) {
				if (pw.valid(providedPassword))
					matched = true;
				else
					authCache.remove(username);
			}
		}
		// Auth
		if (!matched) {
			if (!passwordEncoder.matches(providedPassword, user.getPassword())) {
				logger.debug("Authentication failed: password does not match stored value");

				throw new BadCredentialsException("Bad credentials");
			}
			if (providedPassword != null)
				synchronized (authCache) {
					authCache.put(username, new AuthCacheEntry(providedPassword));
				}
		}

		// Post-auth
		if (!user.isCredentialsNonExpired())
			throw new CredentialsExpiredException(
{'fraction_non_alphanumeric': 0.09166255550074001, 'fraction_numerical': 0.0023680315737543167, 'mean_word_length': 5.472541507024266, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4828487', 'n_tokens_mistral': 2790, 'n_tokens_neox': 2497, 'n_words': 671}