diff options
Diffstat (limited to 'src/main/java/com/p4square/f1oauth')
9 files changed, 1061 insertions, 0 deletions
diff --git a/src/main/java/com/p4square/f1oauth/Attribute.java b/src/main/java/com/p4square/f1oauth/Attribute.java new file mode 100644 index 0000000..64f2507 --- /dev/null +++ b/src/main/java/com/p4square/f1oauth/Attribute.java @@ -0,0 +1,90 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.f1oauth; + +import java.util.Date; + +/** + * F1 Attribute Data. + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class Attribute { + private final String mAttributeName; + private String mId; + private Date mStartDate; + private Date mEndDate; + private String mComment; + + /** + * @param name The attribute name. + */ + public Attribute(final String name) { + mAttributeName = name; + } + + /** + * @return the Attribute name. + */ + public String getAttributeName() { + return mAttributeName; + } + + /** + * @return the id of this specific attribute instance. + */ + public String getId() { + return mId; + } + + /** + * Set the attribute id to id. + */ + public void setId(final String id) { + mId = id; + } + + /** + * @return the start date for the attribute. + */ + public Date getStartDate() { + return mStartDate; + } + + /** + * Set the start date for the attribute. + */ + public void setStartDate(final Date date) { + mStartDate = date; + } + + /** + * @return the end date for the attribute. + */ + public Date getEndDate() { + return mEndDate; + } + + /** + * Set the end date for the attribute. + */ + public void setEndDate(final Date date) { + mEndDate = date; + } + + /** + * @return The comment on the Attribute. + */ + public String getComment() { + return mComment; + } + + /** + * Set the comment on the attribute. + */ + public void setComment(final String comment) { + mComment = comment; + } +} diff --git a/src/main/java/com/p4square/f1oauth/F1API.java b/src/main/java/com/p4square/f1oauth/F1API.java new file mode 100644 index 0000000..a525c3f --- /dev/null +++ b/src/main/java/com/p4square/f1oauth/F1API.java @@ -0,0 +1,56 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.f1oauth; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import com.p4square.restlet.oauth.OAuthException; +import com.p4square.restlet.oauth.OAuthUser; + +/** + * F1 API methods which require an authenticated user. + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public interface F1API { + /** + * Fetch information about a user. + * + * @param user The user to fetch information about. + * @return An F1User object. + */ + F1User getF1User(OAuthUser user) throws OAuthException, IOException; + + /** + * Fetch a list of all attributes ids and names. + * + * @return A Map of attribute name to attribute id. + */ + Map<String, String> getAttributeList() throws F1Exception; + + /** + * Add an attribute to the user. + * + * @param user The user to add the attribute to. + * @param attributeName The attribute to add. + * @param attribute The attribute to add. + */ + boolean addAttribute(String userId, Attribute attribute) throws F1Exception; + + /** + * Return attributes assigned to user. + * + * A user may be assigned multiple attributes with the same name, thus even if + * attributeName is specified, multiple attributes may be returned. + * + * @param userId The user to query. + * @param attributeName A specific attribute to return, null for all. + * @return A list of Attributes + */ + List<Attribute> getAttribute(String userId, String attributeName) throws F1Exception; + +} diff --git a/src/main/java/com/p4square/f1oauth/F1Access.java b/src/main/java/com/p4square/f1oauth/F1Access.java new file mode 100644 index 0000000..c3307f1 --- /dev/null +++ b/src/main/java/com/p4square/f1oauth/F1Access.java @@ -0,0 +1,594 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.f1oauth; + +import java.io.IOException; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; + +import org.apache.log4j.Logger; + +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ChallengeScheme; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.data.Status; +import org.restlet.engine.util.Base64; +import org.restlet.ext.jackson.JacksonRepresentation; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; + +import com.p4square.restlet.oauth.OAuthException; +import com.p4square.restlet.oauth.OAuthHelper; +import com.p4square.restlet.oauth.OAuthUser; +import com.p4square.restlet.oauth.Token; + +/** + * F1 API Access. + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class F1Access { + public enum UserType { + WEBLINK, PORTAL; + } + + private static final Logger LOG = Logger.getLogger(F1Access.class); + + private static final String VERSION_STRING = "/v1/"; + private static final String REQUESTTOKEN_URL = "Tokens/RequestToken"; + private static final String AUTHORIZATION_URL = "Login"; + private static final String ACCESSTOKEN_URL= "Tokens/AccessToken"; + private static final String TRUSTED_ACCESSTOKEN_URL = "/AccessToken"; + + private static final SimpleDateFormat DATE_FORMAT = + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + + private final String mBaseUrl; + private final String mMethod; + + private final OAuthHelper mOAuthHelper; + + private final Map<String, String> mAttributeIdByName; + + private MetricRegistry mMetricRegistry; + + /** + */ + public F1Access(Context context, String consumerKey, String consumerSecret, + String baseUrl, String churchCode, UserType userType) { + + switch (userType) { + case WEBLINK: + mMethod = "WeblinkUser"; + break; + case PORTAL: + mMethod = "PortalUser"; + break; + default: + throw new IllegalArgumentException("Unknown UserType"); + } + + mBaseUrl = "https://" + churchCode + "." + baseUrl + VERSION_STRING; + + // Create the OAuthHelper. This implicitly registers the helper to + // handle outgoing requests which need OAuth authentication. + mOAuthHelper = new OAuthHelper(context, consumerKey, consumerSecret) { + @Override + protected String getRequestTokenUrl() { + return mBaseUrl + REQUESTTOKEN_URL; + } + + @Override + public String getLoginUrl(Token requestToken, String callback) { + String loginUrl = mBaseUrl + mMethod + AUTHORIZATION_URL + + "?oauth_token=" + URLEncoder.encode(requestToken.getToken()); + + if (callback != null) { + loginUrl += "&oauth_callback=" + URLEncoder.encode(callback); + } + + return loginUrl; + } + + @Override + protected String getAccessTokenUrl() { + return mBaseUrl + ACCESSTOKEN_URL; + } + }; + + mAttributeIdByName = new HashMap<>(); + } + + /** + * Set the MetricRegistry to get metrics recorded. + */ + public void setMetricRegistry(MetricRegistry metrics) { + mMetricRegistry = metrics; + } + + /** + * Request an AccessToken for a particular username and password. + * + * This is an F1 extension to OAuth: + * http://developer.fellowshipone.com/docs/v1/Util/AuthDocs.help#2creds + */ + public OAuthUser getAccessToken(String username, String password) throws OAuthException { + Timer.Context timer = getTimer("F1Access.getAccessToken.time"); + boolean success = true; + + try { + Request request = new Request(Method.POST, + mBaseUrl + mMethod + TRUSTED_ACCESSTOKEN_URL); + request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_OAUTH)); + + String base64String = Base64.encode((username + " " + password).getBytes(), false); + request.setEntity(new StringRepresentation(base64String)); + + return mOAuthHelper.processAccessTokenRequest(request); + + } catch (Exception e) { + success = false; + throw e; + + } finally { + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.getAccessToken.success"); + } else { + incrementCounter("F1Access.getAccessToken.failure"); + } + } + } + + /** + * Create a new Account. + * + * @param firstname The user's first name. + * @param lastname The user's last name. + * @param email The user's email address. + * @param redirect The URL to send the user to after confirming his address. + * + * @return true if created, false if the account already exists. + */ + public boolean createAccount(String firstname, String lastname, String email, String redirect) + throws OAuthException { + Timer.Context timer = getTimer("F1Access.createAccount.time"); + boolean success = true; + + try { + String req = String.format("{\n\"account\":{\n\"firstName\":\"%s\",\n" + + "\"lastName\":\"%s\",\n\"email\":\"%s\",\n" + + "\"urlRedirect\":\"%s\"\n}\n}", + firstname, lastname, email, redirect); + + Request request = new Request(Method.POST, mBaseUrl + "Accounts"); + request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_OAUTH)); + request.setEntity(new StringRepresentation(req, MediaType.APPLICATION_JSON)); + + Response response = mOAuthHelper.getResponse(request); + + Status status = response.getStatus(); + if (Status.SUCCESS_NO_CONTENT.equals(status)) { + return true; + + } else if (Status.CLIENT_ERROR_CONFLICT.equals(status)) { + return false; + + } else { + throw new OAuthException(status); + } + + } catch (Exception e) { + success = false; + throw e; + + } finally { + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.createAccount.success"); + } else { + incrementCounter("F1Access.createAccount.failure"); + } + } + } + + /** + * @return An F1API authenticated by the given user. + */ + public F1API getAuthenticatedApi(OAuthUser user) { + return new AuthenticatedApi(user); + } + + private class AuthenticatedApi implements F1API { + private final OAuthUser mUser; + + public AuthenticatedApi(OAuthUser user) { + mUser = user; + } + + /** + * Fetch information about a user. + * + * @param user The user to fetch information about. + * @return An F1User object. + */ + @Override + public F1User getF1User(OAuthUser user) throws OAuthException, IOException { + Timer.Context timer = getTimer("F1Access.getF1User.time"); + boolean success = true; + + try { + Request request = new Request(Method.GET, user.getLocation() + ".json"); + request.setChallengeResponse(mUser.getChallengeResponse()); + Response response = mOAuthHelper.getResponse(request); + + try { + Status status = response.getStatus(); + if (status.isSuccess()) { + JacksonRepresentation<Map> entity = + new JacksonRepresentation<Map>(response.getEntity(), Map.class); + Map data = entity.getObject(); + return new F1User(user, data); + + } else { + throw new OAuthException(status); + } + + } finally { + if (response.getEntity() != null) { + response.release(); + } + } + + } catch (Exception e) { + success = false; + throw e; + + } finally { + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.getF1User.success"); + } else { + incrementCounter("F1Access.getF1User.failure"); + } + } + } + + @Override + public Map<String, String> getAttributeList() throws F1Exception { + // Note: this list is shared by all F1 users. + synchronized (mAttributeIdByName) { + if (mAttributeIdByName.size() == 0) { + Timer.Context timer = getTimer("F1Access.getAttributeList.time"); + boolean success = true; + + try { + // Reload attributes. Maybe it will be there now... + Request request = new Request(Method.GET, + mBaseUrl + "People/AttributeGroups.json"); + request.setChallengeResponse(mUser.getChallengeResponse()); + Response response = mOAuthHelper.getResponse(request); + + Representation representation = response.getEntity(); + try { + Status status = response.getStatus(); + if (status.isSuccess()) { + JacksonRepresentation<Map> entity = + new JacksonRepresentation<Map>(response.getEntity(), Map.class); + + Map attributeGroups = (Map) entity.getObject().get("attributeGroups"); + List<Map> groups = (List<Map>) attributeGroups.get("attributeGroup"); + + for (Map group : groups) { + List<Map> attributes = (List<Map>) group.get("attribute"); + if (attributes != null) { + for (Map attribute : attributes) { + String id = (String) attribute.get("@id"); + String name = ((String) attribute.get("name")); + mAttributeIdByName.put(name.toLowerCase(), id); + LOG.debug("Caching attribute '" + name + + "' with id '" + id + "'"); + } + } + } + } + + } catch (IOException e) { + throw new F1Exception("Could not parse AttributeGroups.", e); + + } finally { + if (representation != null) { + representation.release(); + } + } + + } catch (Exception e) { + success = false; + throw e; + + } finally { + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.getAttributeList.success"); + } else { + incrementCounter("F1Access.getAttributeList.failure"); + } + } + } + + return mAttributeIdByName; + } + } + + /** + * Add an attribute to the user. + * + * @param user The user to add the attribute to. + * @param attributeName The attribute to add. + * @param attribute The attribute to add. + */ + public boolean addAttribute(String userId, Attribute attribute) + throws F1Exception { + + // Get the attribute id. + String attributeId = getAttributeId(attribute.getAttributeName()); + if (attributeId == null) { + throw new F1Exception("Could not find id for " + attribute.getAttributeName()); + } + + // Get Attribute Template + Map attributeTemplate = null; + + Timer.Context timer = getTimer("F1Access.addAttribute.GET.time"); + boolean success = true; + + try { + Request request = new Request(Method.GET, + mBaseUrl + "People/" + userId + "/Attributes/new.json"); + request.setChallengeResponse(mUser.getChallengeResponse()); + Response response = mOAuthHelper.getResponse(request); + + Representation representation = response.getEntity(); + try { + Status status = response.getStatus(); + if (status.isSuccess()) { + JacksonRepresentation<Map> entity = + new JacksonRepresentation<Map>(response.getEntity(), Map.class); + attributeTemplate = entity.getObject(); + + } else { + throw new F1Exception("Failed to retrieve attribute template: " + + status); + } + + } catch (IOException e) { + throw new F1Exception("Could not parse attribute template.", e); + + } finally { + if (representation != null) { + representation.release(); + } + } + } catch (Exception e) { + success = false; + throw e; + + } finally { + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.addAttribute.GET.success"); + } else { + incrementCounter("F1Access.addAttribute.GET.failure"); + } + } + + if (attributeTemplate == null) { + throw new F1Exception("Could not retrieve attribute template."); + } + + // Populate Attribute Template + Map attributeMap = (Map) attributeTemplate.get("attribute"); + Map attributeGroup = (Map) attributeMap.get("attributeGroup"); + + Map<String, String> attributeIdMap = new HashMap<>(); + attributeIdMap.put("@id", attributeId); + attributeGroup.put("attribute", attributeIdMap); + + if (attribute.getStartDate() != null) { + attributeMap.put("startDate", DATE_FORMAT.format(attribute.getStartDate())); + } + + if (attribute.getStartDate() != null) { + attributeMap.put("endDate", DATE_FORMAT.format(attribute.getStartDate())); + } + + attributeMap.put("comment", attribute.getComment()); + + // POST new attribute + Status status; + timer = getTimer("F1Access.addAttribute.POST.time"); + success = true; + + try { + Request request = new Request(Method.POST, + mBaseUrl + "People/" + userId + "/Attributes.json"); + request.setChallengeResponse(mUser.getChallengeResponse()); + request.setEntity(new JacksonRepresentation<Map>(attributeTemplate)); + Response response = mOAuthHelper.getResponse(request); + + Representation representation = response.getEntity(); + try { + status = response.getStatus(); + + if (status.isSuccess()) { + return true; + } + + } finally { + if (representation != null) { + representation.release(); + } + } + } catch (Exception e) { + success = false; + throw e; + + } finally { + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.addAttribute.POST.success"); + } else { + incrementCounter("F1Access.getAccessToken.POST.failure"); + } + } + + LOG.debug("addAttribute failed POST: " + status); + return false; + } + + @Override + public List<Attribute> getAttribute(String userId, String attributeNameFilter) + throws F1Exception { + + Map attributesResponse; + + // Get Attributes + Timer.Context timer = getTimer("F1Access.getAttribute.time"); + boolean success = true; + + try { + Request request = new Request(Method.GET, + mBaseUrl + "People/" + userId + "/Attributes.json"); + request.setChallengeResponse(mUser.getChallengeResponse()); + Response response = mOAuthHelper.getResponse(request); + + Representation representation = response.getEntity(); + try { + Status status = response.getStatus(); + if (status.isSuccess()) { + JacksonRepresentation<Map> entity = + new JacksonRepresentation<Map>(response.getEntity(), Map.class); + attributesResponse = entity.getObject(); + + } else { + throw new F1Exception("Failed to retrieve attributes: " + + status); + } + + } catch (IOException e) { + throw new F1Exception("Could not parse attributes.", e); + + } finally { + if (representation != null) { + representation.release(); + } + } + } catch (Exception e) { + success = false; + throw e; + + } finally { + if (timer != null) { + timer.stop(); + } + if (success) { + incrementCounter("F1Access.getAttribute.success"); + } else { + incrementCounter("F1Access.getAttribute.failure"); + } + } + + // Parse Response + List<Attribute> result = new ArrayList<>(); + + try { + // I feel like I'm writing lisp here... + Map attributesMap = (Map) attributesResponse.get("attributes"); + if (attributesMap == null) { + return result; + } + + List<Map> attributes = (List<Map>) (attributesMap).get("attribute"); + for (Map attributeMap : attributes) { + String id = (String) attributeMap.get("@id"); + String startDate = (String) attributeMap.get("startDate"); + String endDate = (String) attributeMap.get("endDate"); + String comment = (String) attributeMap.get("comment"); + + Map attributeIdMap = (Map) ((Map) attributeMap.get("attributeGroup")) + .get("attribute"); + String attributeName = (String) attributeIdMap.get("name"); + + if (attributeNameFilter == null + || attributeNameFilter.equalsIgnoreCase(attributeName)) { + + Attribute attribute = new Attribute(attributeName); + attribute.setId(id); + if (startDate != null) { + attribute.setStartDate(DATE_FORMAT.parse(startDate)); + } + if (endDate != null) { + attribute.setEndDate(DATE_FORMAT.parse(endDate)); + } + attribute.setComment(comment); + result.add(attribute); + } + } + } catch (Exception e) { + throw new F1Exception("Failed to parse attributes response.", e); + } + + return result; + } + + /** + * @return an attribute id for the given attribute name. + */ + private String getAttributeId(String attributeName) throws F1Exception { + Map<String, String> attributeMap = getAttributeList(); + + return attributeMap.get(attributeName.toLowerCase()); + } + + } + + private Timer.Context getTimer(String name) { + if (mMetricRegistry != null) { + return mMetricRegistry.timer(name).time(); + } else { + return null; + } + } + + private void incrementCounter(String name) { + if (mMetricRegistry != null) { + mMetricRegistry.counter(name).inc(); + } + } +} diff --git a/src/main/java/com/p4square/f1oauth/F1Exception.java b/src/main/java/com/p4square/f1oauth/F1Exception.java new file mode 100644 index 0000000..54c1a77 --- /dev/null +++ b/src/main/java/com/p4square/f1oauth/F1Exception.java @@ -0,0 +1,15 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.f1oauth; + +public class F1Exception extends Exception { + public F1Exception(String message) { + super(message); + } + + public F1Exception(String message, Exception cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/p4square/f1oauth/F1ProgressReporter.java b/src/main/java/com/p4square/f1oauth/F1ProgressReporter.java new file mode 100644 index 0000000..8382020 --- /dev/null +++ b/src/main/java/com/p4square/f1oauth/F1ProgressReporter.java @@ -0,0 +1,57 @@ +package com.p4square.f1oauth; + +import com.p4square.grow.frontend.ProgressReporter; +import org.apache.log4j.Logger; +import org.restlet.security.User; + +import java.util.Date; + +/** + * A ProgressReporter implementation to record progress in F1. + */ +public class F1ProgressReporter implements ProgressReporter { + + private static final Logger LOG = Logger.getLogger(F1ProgressReporter.class); + + private F1Access mF1Access; + + public F1ProgressReporter(final F1Access f1access) { + mF1Access = f1access; + } + + @Override + public void reportAssessmentComplete(final User user, final String level, final Date date, final String results) { + String attributeName = "Assessment Complete - " + level; + Attribute attribute = new Attribute(attributeName); + attribute.setStartDate(date); + attribute.setComment(results); + addAttribute(user, attribute); + } + + @Override + public void reportChapterComplete(final User user, final String chapter, final Date date) { + final String attributeName = "Training Complete - " + chapter; + final Attribute attribute = new Attribute(attributeName); + attribute.setStartDate(date); + addAttribute(user, attribute); + } + + private void addAttribute(final User user, final Attribute attribute) { + if (!(user instanceof F1User)) { + throw new IllegalArgumentException("User must be an F1User, but got " + user.getClass().getName()); + } + + try { + final F1User f1User = (F1User) user; + final F1API f1 = mF1Access.getAuthenticatedApi(f1User); + + if (!f1.addAttribute(user.getIdentifier(), attribute)) { + LOG.error("addAttribute failed for " + user.getIdentifier() + " with attribute " + + attribute.getAttributeName()); + } + } catch (Exception e) { + LOG.error("addAttribute failed for " + user.getIdentifier() + " with attribute " + + attribute.getAttributeName(), e); + } + } +} diff --git a/src/main/java/com/p4square/f1oauth/F1User.java b/src/main/java/com/p4square/f1oauth/F1User.java new file mode 100644 index 0000000..e5ab487 --- /dev/null +++ b/src/main/java/com/p4square/f1oauth/F1User.java @@ -0,0 +1,70 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.f1oauth; + +import java.util.Map; + +import com.p4square.restlet.oauth.OAuthException; +import com.p4square.restlet.oauth.OAuthUser; + +/** + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class F1User extends OAuthUser { + public static final String ID = "@id"; + public static final String FIRST_NAME = "firstName"; + public static final String LAST_NAME = "lastName"; + public static final String ICODE = "@iCode"; + + private final Map mData; + + /** + * Copy the user information from user into a new F1User. + * + * @param user Original user. + * @param data F1 Person Record. + * @throws IllegalStateException if data.get("person") is null. + */ + public F1User(OAuthUser user, Map data) { + super(user.getLocation(), user.getToken()); + + mData = (Map) data.get("person"); + if (mData == null) { + throw new IllegalStateException("Bad data"); + } + + setIdentifier(getString(ID)); + setFirstName(getString(FIRST_NAME)); + setLastName(getString(LAST_NAME)); + } + + /** + * Get a String from the map. + * + * @param key The map key. + * @return The value associated with the key, or null. + */ + public String getString(String key) { + Object blob = get(key); + + if (blob instanceof String) { + return (String) blob; + + } else { + return null; + } + } + + /** + * Fetch an object from the F1 record. + * + * @param key The map key + * @return The object in the map or null. + */ + public Object get(String key) { + return mData.get(key); + } +} diff --git a/src/main/java/com/p4square/f1oauth/FellowshipOneIntegrationDriver.java b/src/main/java/com/p4square/f1oauth/FellowshipOneIntegrationDriver.java new file mode 100644 index 0000000..865f5d6 --- /dev/null +++ b/src/main/java/com/p4square/f1oauth/FellowshipOneIntegrationDriver.java @@ -0,0 +1,55 @@ +package com.p4square.f1oauth; + +import com.codahale.metrics.MetricRegistry; +import com.p4square.grow.config.Config; +import com.p4square.grow.frontend.IntegrationDriver; +import com.p4square.grow.frontend.ProgressReporter; +import org.restlet.Context; +import org.restlet.security.Verifier; + +/** + * The FellowshipOneIntegrationDriver creates implementations of various + * objects to support integration with Fellowship One. + */ +public class FellowshipOneIntegrationDriver implements IntegrationDriver { + + private final Context mContext; + private final MetricRegistry mMetricRegistry; + private final Config mConfig; + private final F1Access mAPI; + + private final ProgressReporter mProgressReporter; + + public FellowshipOneIntegrationDriver(final Context context) { + mContext = context; + mConfig = (Config) context.getAttributes().get("com.p4square.grow.config"); + mMetricRegistry = (MetricRegistry) context.getAttributes().get("com.p4square.grow.metrics"); + + mAPI = new F1Access(context, + mConfig.getString("f1ConsumerKey", ""), + mConfig.getString("f1ConsumerSecret", ""), + mConfig.getString("f1BaseUrl", "staging.fellowshiponeapi.com"), + mConfig.getString("f1ChurchCode", "pfseawa"), + F1Access.UserType.WEBLINK); + mAPI.setMetricRegistry(mMetricRegistry); + + mProgressReporter = new F1ProgressReporter(mAPI); + } + + /** + * @return An F1Access instance. + */ + public F1Access getF1Access() { + return mAPI; + } + + @Override + public Verifier newUserAuthenticationVerifier() { + return new SecondPartyVerifier(mContext, mAPI); + } + + @Override + public ProgressReporter getProgressReporter() { + return mProgressReporter; + } +} diff --git a/src/main/java/com/p4square/f1oauth/SecondPartyAuthenticator.java b/src/main/java/com/p4square/f1oauth/SecondPartyAuthenticator.java new file mode 100644 index 0000000..8deefec --- /dev/null +++ b/src/main/java/com/p4square/f1oauth/SecondPartyAuthenticator.java @@ -0,0 +1,52 @@ +/* + * Copyright 2013 Jesse Morgan + */ + +package com.p4square.f1oauth; + +import org.apache.log4j.Logger; + +import com.p4square.restlet.oauth.OAuthException; +import com.p4square.restlet.oauth.OAuthUser; + +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.security.Authenticator; + +/** + * Restlet Authenticator for 2nd + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class SecondPartyAuthenticator extends Authenticator { + private static final Logger LOG = Logger.getLogger(SecondPartyAuthenticator.class); + + private final F1Access mHelper; + + public SecondPartyAuthenticator(Context context, boolean optional, F1Access helper) { + super(context, optional); + + mHelper = helper; + } + + protected boolean authenticate(Request request, Response response) { + if (request.getChallengeResponse() == null) { + return false; // no credentials + } + + String username = request.getChallengeResponse().getIdentifier(); + String password = new String(request.getChallengeResponse().getSecret()); + + try { + OAuthUser user = mHelper.getAccessToken(username, password); + request.getClientInfo().setUser(user); + + return true; + + } catch (OAuthException e) { + LOG.info("OAuth Exception: " + e); + } + + return false; // Invalid credentials + } +} diff --git a/src/main/java/com/p4square/f1oauth/SecondPartyVerifier.java b/src/main/java/com/p4square/f1oauth/SecondPartyVerifier.java new file mode 100644 index 0000000..882c7e7 --- /dev/null +++ b/src/main/java/com/p4square/f1oauth/SecondPartyVerifier.java @@ -0,0 +1,72 @@ +/* + * Copyright 2013 Jesse Morgan + */ + +package com.p4square.f1oauth; + +import java.io.IOException; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.p4square.restlet.oauth.OAuthException; +import com.p4square.restlet.oauth.OAuthUser; + +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; +import org.restlet.data.Method; +import org.restlet.data.Status; +import org.restlet.ext.jackson.JacksonRepresentation; +import org.restlet.security.Verifier; + +/** + * Restlet Verifier for F1 2nd Party Authentication + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class SecondPartyVerifier implements Verifier { + private static final Logger LOG = Logger.getLogger(SecondPartyVerifier.class); + + private final Restlet mDispatcher; + private final F1Access mHelper; + + public SecondPartyVerifier(Context context, F1Access helper) { + if (helper == null) { + throw new IllegalArgumentException("Helper can not be null."); + } + + mDispatcher = context.getClientDispatcher(); + mHelper = helper; + } + + @Override + public int verify(Request request, Response response) { + if (request.getChallengeResponse() == null) { + return RESULT_MISSING; // no credentials + } + + String username = request.getChallengeResponse().getIdentifier(); + String password = new String(request.getChallengeResponse().getSecret()); + + try { + OAuthUser ouser = mHelper.getAccessToken(username, password); + + // Once we have a user, fetch the people record to get the user id. + F1User user = mHelper.getAuthenticatedApi(ouser).getF1User(ouser); + user.setEmail(username); + + // This seems like a hack... but it'll work + request.getClientInfo().setUser(user); + + return RESULT_VALID; + + } catch (Exception e) { + LOG.info("OAuth Exception: " + e, e); + } + + return RESULT_INVALID; // Invalid credentials + } + +} |