diff --git a/src/main/java/org/scribe/builder/api/ForceApi.java b/src/main/java/org/scribe/builder/api/ForceApi.java new file mode 100644 index 000000000..68c907a7f --- /dev/null +++ b/src/main/java/org/scribe/builder/api/ForceApi.java @@ -0,0 +1,123 @@ +/** + * The MIT License + * + * Copyright (c) 2011, salesforce.com, inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +// This was modified. Original is at: https://github.com/tkral/scribe-java/blob/996ae023f4b71a30bfe94c38f4e98fbdde2ccd35/src/main/java/org/scribe/builder/api/ForceDotComApi.java +package org.scribe.builder.api; + + +import org.scribe.extractors.AccessTokenExtractor; +import org.scribe.extractors.ForceTokenExtractor; +import org.scribe.model.OAuthConfig; +import org.scribe.model.OAuthConstants; +import org.scribe.model.OAuthRequest; +import org.scribe.model.Token; +import org.scribe.model.Verb; +import org.scribe.oauth.OAuth20ServiceImpl; +import org.scribe.oauth.OAuthService; +import org.scribe.utils.OAuthEncoder; +import org.scribe.utils.Preconditions; + +/** + * This is to use the Force.com (or SalesForce.com) OAuth APIs. + *

+ * This class uses the production instance, which is the default. If you want the Sandbox or PreRelease instance, use ForceAPI.Sandbox.class or + * ForceAPI.PreRelease.class respectively. + * + * @author Tim Kral, Ryan Shillington + */ +public class ForceApi extends DefaultApi20 +{ + + private static final String AUTHORIZE_URL_PATH = "/services/oauth2/authorize?response_type=code&client_id=%s&redirect_uri=%s"; + private static final String SCOPED_AUTHORIZE_URL_PATH = AUTHORIZE_URL_PATH + "&scope=%s"; + private static final String ACCESS_URL_PATH = "/services/oauth2/token?grant_type=authorization_code"; + + protected String baseURL = "https://login.salesforce.com"; + + public static class Sandbox extends ForceApi + { + public Sandbox() + { + baseURL = "https://test.salesforce.com"; + } + } + + public static class PreRelease extends ForceApi + { + public PreRelease() + { + baseURL = "https://prerellogin.pre.salesforce.com"; + } + } + + public ForceApi() { } + + @Override + public String getAccessTokenEndpoint() + { + return baseURL + ACCESS_URL_PATH; + } + + @Override + public AccessTokenExtractor getAccessTokenExtractor() + { + return new ForceTokenExtractor(); + } + + @Override + public Verb getAccessTokenVerb() + { + return Verb.POST; + } + + @Override + public String getAuthorizationUrl(OAuthConfig config) + { + Preconditions.checkValidUrl(config.getCallback(), "Must provide a valid url as callback. Force.com does not support OOB"); + + if (config.hasScope()) + { + return String.format(baseURL + SCOPED_AUTHORIZE_URL_PATH, config.getApiKey(), OAuthEncoder.encode(config.getCallback()), + OAuthEncoder.encode(config.getScope())); + } else + { + return String.format(baseURL + AUTHORIZE_URL_PATH, config.getApiKey(), OAuthEncoder.encode(config.getCallback())); + } + } + + @Override + public OAuthService createService(OAuthConfig config) + { + return new OAuth20ServiceImpl(this, config) + { + @Override + /** + * This signs requests in the header where the Force.com * OAuth service expects to find it. + */ + public void signRequest(Token accessToken, OAuthRequest request) + { + request.addHeader(OAuthConstants.HEADER, "Bearer " + accessToken.getToken()); + } + }; + } +} diff --git a/src/main/java/org/scribe/extractors/ForceTokenExtractor.java b/src/main/java/org/scribe/extractors/ForceTokenExtractor.java new file mode 100644 index 000000000..c74b906f5 --- /dev/null +++ b/src/main/java/org/scribe/extractors/ForceTokenExtractor.java @@ -0,0 +1,102 @@ +package org.scribe.extractors; + +import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.scribe.exceptions.OAuthException; +import org.scribe.model.Token; +import org.scribe.utils.Preconditions; + +/** + * Extractor for Force.com OAuth tokens. + * + * @author Tim Kral, Ryan Shillington + */ +public class ForceTokenExtractor implements AccessTokenExtractor +{ + + private Pattern forceTokenPattern = + Pattern.compile("\"id\":\"(\\S*?)\",\"issued_at\":\"(\\d*?)\",\"scope\":\"(\\S*?)\"," + + "\"instance_url\":\"(\\S*?)\",\"signature\":\"(\\S*?)\",\"access_token\":\"(\\S*?)\""); + + @Override + public Token extract(String response) + { + Preconditions.checkEmptyString(response, "Cannot extract a token from a null or empty String"); + Matcher matcher = forceTokenPattern.matcher(response); + if (matcher.find()) + { + return new ForceToken( + matcher.group(1) /*id*/, matcher.group(2) /*issuedAt*/, + matcher.group(3) /*scope*/, matcher.group(4) /*instanceUrl*/, + matcher.group(5) /*signature*/, matcher.group(6) /*accessToken*/, + response); + } else + { + throw new OAuthException("Cannot extract a Force.com access token. Response was: " + response); + } + } + + /** + * Force.com OAuth token. + *

+ * This contains extra information from the Force.com OAuth service: + *

+ * + * @author Tim Kral, Ryan Shillington + */ + public static class ForceToken extends Token + { + + private static final long serialVersionUID = -1522491125878959187L; + + private final String id; + private final Date issuedAt; + private final String scope; + private final String instanceUrl; + private final String signature; + + public ForceToken(String id, String issuedAtStr, String scope, String instanceUrl, String signature, + String token, String rawResponse) + { + super(token, token, rawResponse); + this.scope = scope; + this.id = id; + this.issuedAt = new Date(Long.parseLong(issuedAtStr)); + this.instanceUrl = instanceUrl; + this.signature = signature; + } + + public String getId() + { + return id; + } + + public String getScope() + { + return scope; + } + + public Date getIssuedAt() + { + return issuedAt; + } + + public String getInstanceUrl() + { + return instanceUrl; + } + + public String getSignature() + { + return signature; + } + } + +}