package de.is24.infrastructure.gridfs.http.security;
import de.is24.infrastructure.gridfs.http.storage.FileDescriptor;
import de.is24.infrastructure.gridfs.http.utils.HostName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static java.util.Collections.synchronizedSet;
import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.springframework.util.StringUtils.commaDelimitedListToSet;
import static org.springframework.util.StringUtils.trimAllWhitespace;
@Component
@ManagedResource
public class ProtectedRepoAccessEvaluator {
private static final Logger LOGGER = LoggerFactory.getLogger(ProtectedRepoAccessEvaluator.class);
private final Set<String> protectedRepos;
private List<IpRange> whiteListedIpRanges = new ArrayList<>();
@Autowired
public ProtectedRepoAccessEvaluator(
@Value("${security.protectedRepos:}") String protectedRepos,
@Value("${security.protectedRepoWhiteListedIpRanges:}") String protectedRepoWhiteListedIpRanges) {
this.protectedRepos = synchronizedSet(ipRanges(protectedRepos));
if (isNotBlank(protectedRepoWhiteListedIpRanges)) {
whiteListedIpRanges.addAll(
ipRanges(protectedRepoWhiteListedIpRanges).stream().map(IpRange::new).collect(toList()));
}
}
private Set<String> ipRanges(String protectedRepoWhiteListedIpRanges) {
return commaDelimitedListToSet(trimAllWhitespace(protectedRepoWhiteListedIpRanges));
}
public boolean isAllowedPropagationRepo(String repo) {
return !protectedRepos.contains(repo);
}
public boolean isAllowed(FileDescriptor fileDescriptor, Authentication authentication) {
if (isAuthenticatedUser(authentication)) {
LOGGER.debug("...allowed because authenticated user.");
return true;
}
if (isBackendCall(authentication)) {
LOGGER.debug("...allowed because backend call.");
return true;
}
if (isNotCallOnProtectedRepo(fileDescriptor)) {
LOGGER.debug("...allowed because unprotected repo.");
return true;
}
HostName remoteHost = ((AuthenticationDetails) (authentication.getDetails())).getRemoteHost();
if (isAllowedWebCallOnProtectedRepo(fileDescriptor, remoteHost)) {
LOGGER.debug("...allowed because authorized call to protected repo.");
return true;
}
return false;
}
public boolean isBackendCall(Authentication authentication) {
if (hasAuthenticationDetails(authentication)) {
return !((AuthenticationDetails) (authentication.getDetails())).isWebRequest();
}
logUnknownAuthentication(authentication);
return true;
}
private boolean isAuthenticatedUser(Authentication authentication) {
return authentication != null && authentication instanceof UsernamePasswordAuthenticationToken;
}
private static boolean hasAuthenticationDetails(Authentication authentication) {
return (authentication != null) && (authentication.getDetails() instanceof AuthenticationDetails);
}
private boolean isNotCallOnProtectedRepo(FileDescriptor fileDescriptor) {
return fileDescriptor.getArch().equals("repodata") || !protectedRepos.contains(fileDescriptor.getRepo());
}
public boolean isAllowedWebCallOnProtectedRepo(FileDescriptor fileDescriptor, HostName remoteHostName) {
LOGGER.info("check access permission for {} to {}", remoteHostName, fileDescriptor.getPath());
if (remoteHostName.isIp()) {
LOGGER.debug("..is IP...");
if (!isAllowedIp(remoteHostName.getName())) {
LOGGER.info("... ip not in whitelist: deny");
return false;
}
} else if (!fileDescriptor.getFilename().contains(remoteHostName.getShortName())) {
LOGGER.info("... not ip, not matching: deny");
return false;
}
return true;
}
public void logUnknownAuthentication(Authentication authentication) {
if ((authentication == null) || (authentication.getDetails() == null)) {
LOGGER.warn("encountered null authentication or null authentication details");
} else {
LOGGER.warn("encountered unexpected authentication details type {}",
authentication.getDetails().getClass().getName());
}
}
private boolean isAllowedIp(String ip) {
for (IpRange ipRange : whiteListedIpRanges) {
if (ipRange.isIn(ip)) {
return true;
}
}
return false;
}
@ManagedOperation
public void addProtectedRepo(String repoName) {
protectedRepos.add(repoName);
}
@ManagedOperation
public Set<String> getProtectedRepos() {
return Collections.unmodifiableSet(protectedRepos);
}
}