001package com.box.sdk;
002
003import com.eclipsesource.json.Json;
004import com.eclipsesource.json.JsonObject;
005import java.io.UnsupportedEncodingException;
006import java.net.MalformedURLException;
007import java.net.URL;
008import java.net.URLEncoder;
009
010/**
011 * Represents an authenticated transactional connection to the Box API.
012 *
013 * <p>This class handles everything for transactional API that isn't already handled by BoxAPIConnection.</p>
014 */
015public class BoxTransactionalAPIConnection extends BoxAPIConnection {
016
017    private static final String SUBJECT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
018    private static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
019
020    /**
021     * Constructs a new BoxTransactionalAPIConnection that authenticates with an access token.
022     *
023     * @param accessToken a transactional auth access token.
024     */
025    public BoxTransactionalAPIConnection(String accessToken) {
026        super(accessToken);
027        super.setAutoRefresh(false);
028    }
029
030    /**
031     * Request a scoped transactional token.
032     *
033     * @param accessToken application access token.
034     * @param scope       scope of transactional token.
035     * @return a BoxAPIConnection which can be used to perform transactional requests.
036     */
037    public static BoxAPIConnection getTransactionConnection(String accessToken, String scope) {
038        return BoxTransactionalAPIConnection.getTransactionConnection(accessToken, scope, null);
039    }
040
041    /**
042     * Request a scoped transactional token for a particular resource.
043     *
044     * @param accessToken application access token.
045     * @param scope       scope of transactional token.
046     * @param resource    resource transactional token has access to.
047     * @return a BoxAPIConnection which can be used to perform transactional requests.
048     */
049    public static BoxAPIConnection getTransactionConnection(String accessToken, String scope, String resource) {
050        BoxAPIConnection apiConnection = new BoxAPIConnection(accessToken);
051
052        URL url;
053        try {
054            url = new URL(apiConnection.getTokenURL());
055        } catch (MalformedURLException e) {
056            assert false : "An invalid token URL indicates a bug in the SDK.";
057            throw new RuntimeException("An invalid token URL indicates a bug in the SDK.", e);
058        }
059
060        String urlParameters;
061        try {
062            urlParameters = String.format("grant_type=%s&subject_token=%s&subject_token_type=%s&scope=%s", GRANT_TYPE,
063                URLEncoder.encode(accessToken, "UTF-8"), SUBJECT_TOKEN_TYPE, URLEncoder.encode(scope, "UTF-8"));
064
065            if (resource != null) {
066                urlParameters += "&resource=" + URLEncoder.encode(resource, "UTF-8");
067            }
068        } catch (UnsupportedEncodingException e) {
069            throw new BoxAPIException(
070                "An error occurred while attempting to encode url parameters for a transactional token request"
071            );
072        }
073
074        // authentication uses form url encoded params but response is JSON
075        BoxAPIRequest request = new BoxAPIRequest(apiConnection, url, "POST");
076        request.shouldAuthenticate(false);
077        request.setBody(urlParameters);
078
079        try (BoxJSONResponse response = (BoxJSONResponse) request.send()) {
080            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
081
082            final String fileToken = responseJSON.get("access_token").asString();
083            BoxTransactionalAPIConnection transactionConnection = new BoxTransactionalAPIConnection(fileToken);
084            transactionConnection.setExpires(responseJSON.get("expires_in").asLong() * 1000);
085
086            return transactionConnection;
087        }
088    }
089
090    /**
091     * Disabling the non-Box Developer Edition authenticate method.
092     *
093     * @param authCode an auth code obtained from the first half of the OAuth process.
094     * @throws UnsupportedOperationException Box Transactional API does not support authentication with an auth code
095     */
096    @Override
097    public void authenticate(String authCode) {
098        throw new UnsupportedOperationException(
099            "BoxTransactionalAPIConnection does not support the authenticate method."
100        );
101    }
102
103    /**
104     * BoxTransactionalAPIConnection can never refresh.
105     *
106     * @return false always.
107     */
108    @Override
109    public boolean canRefresh() {
110        return false;
111    }
112
113    /**
114     * Auto refresh is not available for transactional auth.
115     *
116     * @param autoRefresh true to enable auto token refresh; otherwise false.
117     * @throws UnsupportedOperationException Box Transactional API tokens can not be refreshed
118     */
119    @Override
120    public void setAutoRefresh(boolean autoRefresh) {
121        throw new UnsupportedOperationException(
122            "BoxTransactionalAPIConnection does not support token refreshing, "
123                + "access tokens can be generated in the developer console."
124        );
125    }
126
127    /**
128     * Transactional auth does not support token refreshes.
129     *
130     * @throws UnsupportedOperationException Box Transactional API tokens can not be refreshed
131     */
132    @Override
133    public void refresh() {
134        throw new UnsupportedOperationException(
135            "BoxTransactionalAPIConnection does not support token refreshing, "
136                + "access tokens can be generated in the developer console."
137        );
138    }
139}