package org.syncany.plugins.transfer.oauth; import java.net.URI; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import com.google.common.base.Charsets; /** * Factory class to generate some common {@link OAuthTokenExtractor}s. * * @author Christian Roth <christian.roth@port17.de> */ public abstract class OAuthTokenExtractors { private static final Logger logger = Logger.getLogger(OAuthTokenExtractors.class.getName()); public static final String RFC_CODE_FIELD = "code"; public static final String RFC_ACCESS_TOKEN_FIELD = "access_token"; public static final String RFC_STATE_FIELD = "state"; /** * Get a common {@link OAuthTokenExtractor} depending on the chosen {@link OAuthMode}. More precisely, this creates a * {@link OAuthTokenExtractors.NamedQueryTokenExtractor} with token field id set to * {@value #RFC_STATE_FIELD} in {@link OAuthMode#SERVER} and {@value #RFC_ACCESS_TOKEN_FIELD} in {@link OAuthMode#BROWSER}. * However, {@value #RFC_STATE_FIELD} is used in both cases to identify a potential CSRF value. * * @param mode {@link OAuthMode} supported by the {@link org.syncany.plugins.transfer.TransferPlugin}. * @return A corresponding {@link OAuthTokenExtractors.NamedQueryTokenExtractor}. */ public static OAuthTokenExtractor newTokenExtractorForMode(OAuthMode mode) { switch (mode) { case BROWSER: return new NamedQueryTokenExtractor(RFC_ACCESS_TOKEN_FIELD, RFC_STATE_FIELD); case SERVER: return new NamedQueryTokenExtractor(RFC_CODE_FIELD, RFC_STATE_FIELD); default: throw new RuntimeException("Unknown OAuth mode"); } } /** * A {@link NamedQueryTokenExtractor} is a simple {@link OAuthTokenExtractor} which looks for a token and a CSRF secret * in the redirect URL. Field names a variables. */ public static class NamedQueryTokenExtractor implements OAuthTokenExtractor { private final String tokenId; private final String stateId; NamedQueryTokenExtractor(String tokenId, String stateId) { this.tokenId = tokenId; this.stateId = stateId; } @Override public OAuthTokenFinish parse(String uriWithToken) throws NoSuchFieldException { List<NameValuePair> params = URLEncodedUtils.parse(URI.create(uriWithToken), Charsets.UTF_8.name()); String token = null; String state = null; for (NameValuePair param : params) { if (tokenId.equalsIgnoreCase(param.getName())) { token = param.getValue(); logger.log(Level.FINE, "Found token in URL " + token); } else if (stateId.equalsIgnoreCase(param.getName())) { state = param.getValue(); logger.log(Level.FINE, "Found state in URL " + state); } } if (token == null || state == null) { throw new NoSuchFieldException(String.format("URI (%s) does not contain token field (%s, %s)", uriWithToken, tokenId, stateId)); } return new OAuthTokenFinish(token, state); } } }