001package org.syncany.plugins.transfer.oauth; 002 003import java.net.URI; 004import java.util.List; 005import java.util.logging.Level; 006import java.util.logging.Logger; 007 008import org.apache.http.NameValuePair; 009import org.apache.http.client.utils.URLEncodedUtils; 010import com.google.common.base.Charsets; 011 012/** 013 * Factory class to generate some common {@link OAuthTokenExtractor}s. 014 * 015 * @author Christian Roth (christian.roth@port17.de) 016 */ 017public abstract class OAuthTokenExtractors { 018 private static final Logger logger = Logger.getLogger(OAuthTokenExtractors.class.getName()); 019 020 public static final String RFC_CODE_FIELD = "code"; 021 public static final String RFC_ACCESS_TOKEN_FIELD = "access_token"; 022 public static final String RFC_STATE_FIELD = "state"; 023 024 /** 025 * Get a common {@link OAuthTokenExtractor} depending on the chosen {@link OAuthMode}. More precisely, this creates a 026 * {@link OAuthTokenExtractors.NamedQueryTokenExtractor} with token field id set to 027 * {@value #RFC_STATE_FIELD} in {@link OAuthMode#SERVER} and {@value #RFC_ACCESS_TOKEN_FIELD} in {@link OAuthMode#BROWSER}. 028 * However, {@value #RFC_STATE_FIELD} is used in both cases to identify a potential CSRF value. 029 * 030 * @param mode {@link OAuthMode} supported by the {@link org.syncany.plugins.transfer.TransferPlugin}. 031 * @return A corresponding {@link OAuthTokenExtractors.NamedQueryTokenExtractor}. 032 */ 033 public static OAuthTokenExtractor newTokenExtractorForMode(OAuthMode mode) { 034 switch (mode) { 035 case BROWSER: 036 return new NamedQueryTokenExtractor(RFC_ACCESS_TOKEN_FIELD, RFC_STATE_FIELD); 037 038 case SERVER: 039 return new NamedQueryTokenExtractor(RFC_CODE_FIELD, RFC_STATE_FIELD); 040 041 default: 042 throw new RuntimeException("Unknown OAuth mode"); 043 } 044 } 045 046 /** 047 * A {@link NamedQueryTokenExtractor} is a simple {@link OAuthTokenExtractor} which looks for a token and a CSRF secret 048 * in the redirect URL. Field names a variables. 049 */ 050 public static class NamedQueryTokenExtractor implements OAuthTokenExtractor { 051 private final String tokenId; 052 private final String stateId; 053 054 NamedQueryTokenExtractor(String tokenId, String stateId) { 055 this.tokenId = tokenId; 056 this.stateId = stateId; 057 } 058 059 @Override 060 public OAuthTokenFinish parse(String uriWithToken) throws NoSuchFieldException { 061 List<NameValuePair> params = URLEncodedUtils.parse(URI.create(uriWithToken), Charsets.UTF_8.name()); 062 063 String token = null; 064 String state = null; 065 066 for (NameValuePair param : params) { 067 if (tokenId.equalsIgnoreCase(param.getName())) { 068 token = param.getValue(); 069 logger.log(Level.FINE, "Found token in URL " + token); 070 } 071 else if (stateId.equalsIgnoreCase(param.getName())) { 072 state = param.getValue(); 073 logger.log(Level.FINE, "Found state in URL " + state); 074 } 075 } 076 077 if (token == null || state == null) { 078 throw new NoSuchFieldException(String.format("URI (%s) does not contain token field (%s, %s)", uriWithToken, tokenId, stateId)); 079 } 080 081 return new OAuthTokenFinish(token, state); 082 } 083 } 084 085}