You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by sh...@apache.org on 2015/06/30 20:38:17 UTC

svn commit: r1688503 [2/2] - in /ofbiz/trunk: ./ specialpurpose/ specialpurpose/ecommerce/webapp/ecommerce/customer/ specialpurpose/ecommerce/webapp/ecomseo/WEB-INF/ specialpurpose/ecommerce/widget/ specialpurpose/passport/ specialpurpose/passport/conf...

Added: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubAuthenticator.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubAuthenticator.java?rev=1688503&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubAuthenticator.java (added)
+++ ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubAuthenticator.java Tue Jun 30 18:38:16 2015
@@ -0,0 +1,416 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.ofbiz.passport.user;
+
+import java.util.Locale;
+import java.util.Map;
+import java.io.IOException;
+import java.io.Serializable;
+import java.sql.Timestamp;
+
+import javax.transaction.Transaction;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.cookie.CookiePolicy;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.ofbiz.passport.event.GitHubEvents;
+import org.ofbiz.passport.user.GitHubUserGroupMapper;
+import org.ofbiz.passport.util.PassportUtil;
+import org.ofbiz.common.authentication.api.Authenticator;
+import org.ofbiz.common.authentication.api.AuthenticatorException;
+import org.ofbiz.service.LocalDispatcher;
+import org.ofbiz.service.GenericServiceException;
+import org.ofbiz.service.ServiceUtil;
+import org.ofbiz.entity.Delegator;
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.GenericEntityException;
+import org.ofbiz.entity.transaction.TransactionUtil;
+import org.ofbiz.entity.transaction.GenericTransactionException;
+import org.ofbiz.entity.util.EntityUtil;
+import org.ofbiz.base.conversion.ConversionException;
+import org.ofbiz.base.conversion.JSONConverters.JSONToMap;
+import org.ofbiz.base.lang.JSON;
+import org.ofbiz.base.util.UtilProperties;
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.UtilMisc;
+import org.ofbiz.base.util.UtilDateTime;
+import org.ofbiz.base.util.UtilValidate;
+
+import javolution.util.FastMap;
+
+/**
+ * GitHub OFBiz Authenticator
+ */
+public class GitHubAuthenticator implements Authenticator {
+
+    private static final String module = GitHubAuthenticator.class.getName();
+
+    public static final String props = "gitHubAuth.properties";
+
+    public static final String resource = "PassportUiLabels";
+
+    protected LocalDispatcher dispatcher;
+
+    protected Delegator delegator;
+
+    /**
+     * Method called when authenticator is first initialized (the delegator
+     * object can be obtained from the LocalDispatcher)
+     *
+     * @param dispatcher The ServiceDispatcher to use for this Authenticator
+     */
+    public void initialize(LocalDispatcher dispatcher) {
+        this.dispatcher = dispatcher;
+        this.delegator = dispatcher.getDelegator();
+    }
+
+    /**
+     * Method to authenticate a user.
+     * 
+     * For GitHub users, we only check if the username(userLoginId) exists an 
+     * externalAuthId, and the externalAuthId has a valid accessToken in 
+     * GitHubUser entity.
+     *
+     * @param username      User's username
+     * @param password      User's password
+     * @param isServiceAuth true if authentication is for a service call
+     * @return true if the user is authenticated
+     * @throws org.ofbiz.common.authentication.api.AuthenticatorException
+     *          when a fatal error occurs during authentication
+     */
+    public boolean authenticate(String userLoginId, String password, boolean isServiceAuth) throws AuthenticatorException {
+        Map<String, Object> user = null;
+        GetMethod getMethod = null;
+        try {
+            GenericValue userLogin = delegator.findOne("UserLogin", UtilMisc.toMap("userLoginId", userLoginId), false);
+            String externalAuthId = userLogin.getString("externalAuthId");
+            GenericValue gitHubUser = delegator.findOne("GitHubUser", UtilMisc.toMap("gitHubUserId", externalAuthId), false);
+            if (UtilValidate.isNotEmpty(gitHubUser)) {
+                String accessToken = gitHubUser.getString("accessToken");
+                String tokenType = gitHubUser.getString("tokenType");
+                if (UtilValidate.isNotEmpty(accessToken)) {
+                    getMethod = new GetMethod(GitHubEvents.ApiEndpoint + GitHubEvents.UserApiUri);
+                    user = GitHubAuthenticator.getUserInfo(getMethod, accessToken, tokenType, Locale.getDefault());
+                }
+            }
+        } catch (GenericEntityException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (HttpException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (IOException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (AuthenticatorException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } finally {
+            if (getMethod != null) {
+                getMethod.releaseConnection();
+            }
+        }
+
+        Debug.logInfo("GitHub auth called; returned user info: " + user, module);
+        return user != null;
+    }
+
+    /**
+     * Logs a user out
+     *
+     * @param username User's username
+     * @throws org.ofbiz.common.authentication.api.AuthenticatorException
+     *          when logout fails
+     */
+    public void logout(String username) throws AuthenticatorException {
+    }
+
+    /**
+     * Reads user information and syncs it to OFBiz (i.e. UserLogin, Person, etc)
+     *
+     * @param userLoginId
+     * @throws org.ofbiz.common.authentication.api.AuthenticatorException
+     *          user synchronization fails
+     */
+    public void syncUser(String userLoginId) throws AuthenticatorException {
+        Map<String, Object> userMap = getGitHubUserinfo(userLoginId);
+        GenericValue system;
+        try {
+            system = delegator.findOne("UserLogin", UtilMisc.toMap("userLoginId", "system"), true);
+        } catch (GenericEntityException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        }
+
+        GenericValue userLogin;
+        try {
+            userLogin = EntityUtil.getFirst(delegator.findByAnd("UserLogin", UtilMisc.toMap("externalAuthId", (String) userMap.get("id")), null, false));
+        } catch (GenericEntityException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        }
+
+        // suspend the current transaction and load the user
+        Transaction parentTx = null;
+        boolean beganTransaction = false;
+
+        try {
+            try {
+                parentTx = TransactionUtil.suspend();
+            } catch (GenericTransactionException e) {
+                Debug.logError(e, "Could not suspend transaction: " + e.getMessage(), module);
+            }
+
+            try {
+                beganTransaction = TransactionUtil.begin();
+
+                if (userLogin == null) {
+                    // create the user
+                    createUser(userMap, system);
+                } else {
+                    // update the user information
+                    updateUser(userMap, system, userLogin);
+                }
+
+            } catch (GenericTransactionException e) {
+                Debug.logError(e, "Could not suspend transaction: " + e.getMessage(), module);
+            } finally {
+                try {
+                    TransactionUtil.commit(beganTransaction);
+                } catch (GenericTransactionException e) {
+                    Debug.logError(e, "Could not commit nested transaction: " + e.getMessage(), module);
+                }
+            }
+        } finally {
+            // resume/restore parent transaction
+            if (parentTx != null) {
+                try {
+                    TransactionUtil.resume(parentTx);
+                    Debug.logVerbose("Resumed the parent transaction.", module);
+                } catch (GenericTransactionException e) {
+                    Debug.logError(e, "Could not resume parent nested transaction: " + e.getMessage(), module);
+                }
+            }
+        }
+    }
+
+    private Map<String, Object> getGitHubUserinfo(String userLoginId) throws AuthenticatorException {
+        Map<String, Object> user = null;
+        GetMethod getMethod = null;
+        try {
+            GenericValue userLogin = delegator.findOne("UserLogin", UtilMisc.toMap("userLoginId", userLoginId), false);
+            String externalAuthId = userLogin.getString("externalAuthId");
+            GenericValue gitHubUser = delegator.findOne("GitHubUser", UtilMisc.toMap("gitHubUserId", externalAuthId), false);
+            if (UtilValidate.isNotEmpty(gitHubUser)) {
+                String accessToken = gitHubUser.getString("accessToken");
+                String tokenType = gitHubUser.getString("tokenType");
+                if (UtilValidate.isNotEmpty(accessToken)) {
+                    getMethod = new GetMethod(GitHubEvents.ApiEndpoint + GitHubEvents.UserApiUri);
+                    user = getUserInfo(getMethod, accessToken, tokenType, Locale.getDefault());
+                }
+            }
+        } catch (GenericEntityException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (HttpException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (IOException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (AuthenticatorException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } finally {
+            if (getMethod != null) {
+                getMethod.releaseConnection();
+            }
+        }
+        return user;
+    }
+
+    public String createUser(Map<String, Object> userMap) throws AuthenticatorException {
+        GenericValue system;
+        try {
+            system = delegator.findOne("UserLogin", UtilMisc.toMap("userLoginId", "system"), true);
+        } catch (GenericEntityException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        }
+        return createUser(userMap, system);
+    }
+    
+    private String createUser(Map<String, Object> userMap, GenericValue system) throws AuthenticatorException {
+        // create person + userLogin
+        Map<String, Serializable> createPersonUlMap = FastMap.newInstance();
+        String userLoginId = delegator.getNextSeqId("UserLogin");
+        if (userMap.containsKey("name")) {
+            // use github's name as OFBiz's lastName
+            createPersonUlMap.put("lastName", (String) userMap.get("name"));
+        }
+        if (userMap.containsKey("login")) {
+            createPersonUlMap.put("externalAuthId", (String) userMap.get("login"));
+        }
+        // createPersonUlMap.put("externalId", user.getUserId());
+        createPersonUlMap.put("userLoginId", userLoginId);
+        createPersonUlMap.put("currentPassword", "[EXTERNAL]");
+        createPersonUlMap.put("currentPasswordVerify", "[EXTERNAL]");
+        createPersonUlMap.put("userLogin", system);
+        Map<String, Object> createPersonResult;
+        try {
+            createPersonResult = dispatcher.runSync("createPersonAndUserLogin", createPersonUlMap);
+        } catch (GenericServiceException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        }
+        if (ServiceUtil.isError(createPersonResult)) {
+            throw new AuthenticatorException(ServiceUtil.getErrorMessage(createPersonResult));
+        }
+        String partyId = (String) createPersonResult.get("partyId");
+
+        // give this person a role of CUSTOMER
+        GenericValue partyRole = delegator.makeValue("PartyRole", UtilMisc.toMap("partyId", partyId, "roleTypeId", "CUSTOMER"));
+        try {
+            delegator.create(partyRole);
+        } catch (GenericEntityException e) {
+            Debug.logError(e, module);
+            throw new AuthenticatorException(e.getMessage(), e);
+        }
+
+        // create email
+        if (userMap.containsKey("email")) {
+            Map<String, Serializable> createEmailMap = FastMap.newInstance();
+            createEmailMap.put("emailAddress", (String) userMap.get("email"));
+            createEmailMap.put("contactMechPurposeTypeId", "PRIMARY_EMAIL");
+            createEmailMap.put("partyId", partyId);
+            createEmailMap.put("userLogin", system);
+            Map<String, Object> createEmailResult;
+            try {
+                createEmailResult = dispatcher.runSync("createPartyEmailAddress", createEmailMap);
+            } catch (GenericServiceException e) {
+                throw new AuthenticatorException(e.getMessage(), e);
+            }
+            if (ServiceUtil.isError(createEmailResult)) {
+                throw new AuthenticatorException(ServiceUtil.getErrorMessage(createEmailResult));
+            }
+        }
+
+        // create security group(s)
+        Timestamp now = UtilDateTime.nowTimestamp();
+        for (String securityGroup : (new GitHubUserGroupMapper(new String[] {(String) userMap.get("type")}).getSecurityGroups())) {
+            // check and make sure the security group exists
+            GenericValue secGroup = null;
+            try {
+                secGroup = delegator.findOne("SecurityGroup", UtilMisc.toMap("groupId", securityGroup), true);
+            } catch (GenericEntityException e) {
+                Debug.logError(e, e.getMessage(), module);
+            }
+
+            // add it to the user if it exists
+            if (secGroup != null) {
+                Map<String, Serializable> createSecGrpMap = FastMap.newInstance();
+                createSecGrpMap.put("userLoginId", userLoginId);
+                createSecGrpMap.put("groupId", securityGroup);
+                createSecGrpMap.put("fromDate", now);
+                createSecGrpMap.put("userLogin", system);
+
+                Map<String, Object> createSecGrpResult;
+                try {
+                    createSecGrpResult = dispatcher.runSync("addUserLoginToSecurityGroup", createSecGrpMap);
+                } catch (GenericServiceException e) {
+                    throw new AuthenticatorException(e.getMessage(), e);
+                }
+                if (ServiceUtil.isError(createSecGrpResult)) {
+                    throw new AuthenticatorException(ServiceUtil.getErrorMessage(createSecGrpResult));
+                }
+            }
+        }
+        return userLoginId;
+    }
+
+    private void updateUser(Map<String, Object> userMap, GenericValue system, GenericValue userLogin) throws AuthenticatorException {
+        // TODO implement me
+    }
+
+    /**
+     * Updates a user's password.
+     *
+     * @param username    User's username
+     * @param password    User's current password
+     * @param newPassword User's new password
+     * @throws org.ofbiz.common.authentication.api.AuthenticatorException
+     *          when update password fails
+     */
+    public void updatePassword(String username, String password, String newPassword) throws AuthenticatorException {
+        Debug.logInfo("Calling GitHub:updatePassword() - ignored!!!", module);
+    }
+
+    /**
+     * Weight of this authenticator (lower weights are run first)
+     *
+     * @return the weight of this Authenicator
+     */
+    public float getWeight() {
+        return 1;
+    }
+
+    /**
+     * Is the user synchronzied back to OFBiz
+     *
+     * @return true if the user record is copied to the OFB database
+     */
+    public boolean isUserSynchronized() {
+        return true;
+    }
+
+    /**
+     * Is this expected to be the only authenticator, if so errors will be thrown when users cannot be found
+     *
+     * @return true if this is expected to be the only Authenticator
+     */
+    public boolean isSingleAuthenticator() {
+        return false;
+    }
+
+    /**
+     * Flag to test if this Authenticator is enabled
+     *
+     * @return true if the Authenticator is enabled
+     */
+    public boolean isEnabled() {
+        return "true".equalsIgnoreCase(UtilProperties.getPropertyValue(props, "github.authenticator.enabled", "true"));
+    }
+
+    public static Map<String, Object> getUserInfo(GetMethod getMethod, String accessToken, String tokenType, Locale locale) throws HttpException, IOException, AuthenticatorException {
+        JSON userInfo = null;
+        HttpClient jsonClient = new HttpClient();
+        HttpMethodParams params = new HttpMethodParams();
+        params.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
+        getMethod.setParams(params);
+        getMethod.setRequestHeader(PassportUtil.AUTHORIZATION_HEADER, tokenType + " " + accessToken);
+        getMethod.setRequestHeader(PassportUtil.ACCEPT_HEADER, "application/json");
+        jsonClient.executeMethod(getMethod);
+        if (getMethod.getStatusCode() == HttpStatus.SC_OK) {
+            Debug.logInfo("Json Response from GitHub: " + getMethod.getResponseBodyAsString(), module);
+            userInfo = JSON.from(getMethod.getResponseBodyAsString());
+        } else {
+            String errMsg = UtilProperties.getMessage(resource, "GetOAuth2AccessTokenError", UtilMisc.toMap("error", getMethod.getResponseBodyAsString()), locale);
+            throw new AuthenticatorException(errMsg);
+        }
+        JSONToMap jsonMap = new JSONToMap();
+        Map<String, Object> userMap;
+        try {
+            userMap = jsonMap.convert(userInfo);
+        } catch (ConversionException e) {
+            throw new AuthenticatorException(e.getMessage());
+        }
+        return userMap;
+    }
+}

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubAuthenticator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubAuthenticator.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubAuthenticator.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubUserGroupMapper.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubUserGroupMapper.java?rev=1688503&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubUserGroupMapper.java (added)
+++ ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubUserGroupMapper.java Tue Jun 30 18:38:16 2015
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.ofbiz.passport.user;
+
+import java.util.List;
+import java.util.Properties;
+import java.util.Arrays;
+import java.util.Set;
+
+import org.ofbiz.base.util.UtilProperties;
+
+import javolution.util.FastList;
+import javolution.util.FastSet;
+
+/**
+ * GitHub UserGroupMapper
+ */
+public class GitHubUserGroupMapper {
+
+    protected List<String> groups;
+
+    public GitHubUserGroupMapper(String[] groups) {
+        this.groups = Arrays.asList(groups);
+    }
+
+    public GitHubUserGroupMapper(String group) {
+        if (groups == null) {
+            groups = FastList.newInstance();
+        }
+        groups.add(group);
+    }
+
+    public Set<String> getSecurityGroups() {
+        Properties props = UtilProperties.getProperties(GitHubAuthenticator.props);
+
+        Set<String> secGroups = FastSet.newInstance();
+        boolean running = true;
+        int index = 1;
+
+        while (running) {
+            String groupStr = (String) props.get("github.group.map." + index);
+            if (groupStr == null) {
+                running = false;
+            } else {
+                String[] groupSplit = groupStr.split("=");
+                if (groupSplit.length == 2) {
+                    if (groups.contains(groupSplit[0])) {
+                        secGroups.add(groupSplit[1]);
+                    }
+                }
+            }
+            index++;
+        }
+        return secGroups;
+    }
+}

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubUserGroupMapper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubUserGroupMapper.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/GitHubUserGroupMapper.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInAuthenticator.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInAuthenticator.java?rev=1688503&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInAuthenticator.java (added)
+++ ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInAuthenticator.java Tue Jun 30 18:38:16 2015
@@ -0,0 +1,471 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.ofbiz.passport.user;
+
+import java.util.Locale;
+import java.util.Map;
+import java.io.IOException;
+import java.io.Serializable;
+import java.sql.Timestamp;
+
+import javax.transaction.Transaction;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.cookie.CookiePolicy;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.ofbiz.passport.event.LinkedInEvents;
+import org.ofbiz.common.authentication.api.Authenticator;
+import org.ofbiz.common.authentication.api.AuthenticatorException;
+import org.ofbiz.service.LocalDispatcher;
+import org.ofbiz.service.GenericServiceException;
+import org.ofbiz.service.ServiceUtil;
+import org.ofbiz.entity.Delegator;
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.GenericEntityException;
+import org.ofbiz.entity.transaction.TransactionUtil;
+import org.ofbiz.entity.transaction.GenericTransactionException;
+import org.ofbiz.entity.util.EntityUtil;
+import org.ofbiz.base.util.UtilProperties;
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.UtilMisc;
+import org.ofbiz.base.util.UtilDateTime;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.base.util.UtilXml;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javolution.util.FastMap;
+
+/**
+ * LinkedIn OFBiz Authenticator
+ */
+public class LinkedInAuthenticator implements Authenticator {
+
+    private static final String module = LinkedInAuthenticator.class.getName();
+
+    public static final String props = "linkedInAuth.properties";
+
+    public static final String resource = "PassportUiLabels";    
+
+    protected LocalDispatcher dispatcher;
+
+    protected Delegator delegator;
+
+    /**
+     * Method called when authenticator is first initialized (the delegator
+     * object can be obtained from the LocalDispatcher)
+     *
+     * @param dispatcher The ServiceDispatcher to use for this Authenticator
+     */
+    public void initialize(LocalDispatcher dispatcher) {
+        this.dispatcher = dispatcher;
+        this.delegator = dispatcher.getDelegator();
+    }
+
+    /**
+     * Method to authenticate a user.
+     * 
+     * For LinkedIn users, we only check if the username(userLoginId) exists an 
+     * externalAuthId, and the externalAuthId has a valid accessToken in 
+     * LinkedInUser entity.
+     *
+     * @param username      User's username
+     * @param password      User's password
+     * @param isServiceAuth true if authentication is for a service call
+     * @return true if the user is authenticated
+     * @throws org.ofbiz.common.authentication.api.AuthenticatorException
+     *          when a fatal error occurs during authentication
+     */
+    public boolean authenticate(String userLoginId, String password, boolean isServiceAuth) throws AuthenticatorException {
+        Document user = null;
+        GetMethod getMethod = null;
+        try {
+            GenericValue userLogin = delegator.findOne("UserLogin", UtilMisc.toMap("userLoginId", userLoginId), false);
+            String externalAuthId = userLogin.getString("externalAuthId");
+            GenericValue linkedInUser = delegator.findOne("LinkedInUser", UtilMisc.toMap("linedInUserId", externalAuthId), false);
+            if (UtilValidate.isNotEmpty(linkedInUser)) {
+                String accessToken = linkedInUser.getString("accessToken");
+                if (UtilValidate.isNotEmpty(accessToken)) {
+                    getMethod = new GetMethod(LinkedInEvents.TokenEndpoint + LinkedInEvents.UserApiUri  + "?oauth2_access_token=" + accessToken);
+                    user = LinkedInAuthenticator.getUserInfo(getMethod, Locale.getDefault());
+                }
+            }
+        } catch (GenericEntityException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (HttpException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (IOException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (AuthenticatorException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (SAXException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (ParserConfigurationException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } finally {
+            if (getMethod != null) {
+                getMethod.releaseConnection();
+            }
+        }
+
+        Debug.logInfo("LinkedIn auth called; returned user info: " + user, module);
+        return user != null;
+    }
+
+    /**
+     * Logs a user out
+     *
+     * @param username User's username
+     * @throws org.ofbiz.common.authentication.api.AuthenticatorException
+     *          when logout fails
+     */
+    public void logout(String username) throws AuthenticatorException {
+    }
+
+    /**
+     * Reads user information and syncs it to OFBiz (i.e. UserLogin, Person, etc)
+     *
+     * @param userLoginId
+     * @throws org.ofbiz.common.authentication.api.AuthenticatorException
+     *          user synchronization fails
+     */
+    public void syncUser(String userLoginId) throws AuthenticatorException {
+        Document user = getLinkedInUserinfo(userLoginId);
+
+        GenericValue system;
+        try {
+            system = delegator.findOne("UserLogin", UtilMisc.toMap("userLoginId", "system"), true);
+        } catch (GenericEntityException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        }
+
+        GenericValue userLogin;
+        try {
+            userLogin = EntityUtil.getFirst(delegator.findByAnd("UserLogin", UtilMisc.toMap("externalAuthId", getLinkedInUserId(user)), null, false));
+        } catch (GenericEntityException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        }
+
+        // suspend the current transaction and load the user
+        Transaction parentTx = null;
+        boolean beganTransaction = false;
+
+        try {
+            try {
+                parentTx = TransactionUtil.suspend();
+            } catch (GenericTransactionException e) {
+                Debug.logError(e, "Could not suspend transaction: " + e.getMessage(), module);
+            }
+
+            try {
+                beganTransaction = TransactionUtil.begin();
+
+                if (userLogin == null) {
+                    // create the user
+                    createUser(user, system);
+                } else {
+                    // update the user information
+                    updateUser(user, system, userLogin);
+                }
+
+            } catch (GenericTransactionException e) {
+                Debug.logError(e, "Could not suspend transaction: " + e.getMessage(), module);
+            } finally {
+                try {
+                    TransactionUtil.commit(beganTransaction);
+                } catch (GenericTransactionException e) {
+                    Debug.logError(e, "Could not commit nested transaction: " + e.getMessage(), module);
+                }
+            }
+        } finally {
+            // resume/restore parent transaction
+            if (parentTx != null) {
+                try {
+                    TransactionUtil.resume(parentTx);
+                    Debug.logVerbose("Resumed the parent transaction.", module);
+                } catch (GenericTransactionException e) {
+                    Debug.logError(e, "Could not resume parent nested transaction: " + e.getMessage(), module);
+                }
+            }
+        }
+    }
+
+    private Document getLinkedInUserinfo(String userLoginId) throws AuthenticatorException {
+        Document user = null;
+        GetMethod getMethod = null;
+        try {
+            GenericValue userLogin = delegator.findOne("UserLogin", UtilMisc.toMap("userLoginId", userLoginId), false);
+            String externalAuthId = userLogin.getString("externalAuthId");
+            GenericValue linkedInUser = delegator.findOne("LinkedInUser", UtilMisc.toMap("linkedInUserId", externalAuthId), false);
+            if (UtilValidate.isNotEmpty(linkedInUser)) {
+                String accessToken = linkedInUser.getString("accessToken");
+                if (UtilValidate.isNotEmpty(accessToken)) {
+                    getMethod = new GetMethod(LinkedInEvents.TokenEndpoint + LinkedInEvents.UserApiUri + "?oauth2_access_token=" + accessToken);
+                    user = getUserInfo(getMethod, Locale.getDefault());
+                }
+            }
+        } catch (GenericEntityException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (HttpException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (IOException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (AuthenticatorException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (SAXException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } catch (ParserConfigurationException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        } finally {
+            if (getMethod != null) {
+                getMethod.releaseConnection();
+            }
+        }
+        return user;
+    }
+
+    public String createUser(Document user) throws AuthenticatorException {
+        GenericValue system;
+        try {
+            system = delegator.findOne("UserLogin", UtilMisc.toMap("userLoginId", "system"), true);
+        } catch (GenericEntityException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        }
+        return createUser(user, system);
+    }
+    
+    private String createUser(Document user, GenericValue system) throws AuthenticatorException {
+        Map<String, String> userInfo = parseLinkedInUserInfo(user);
+
+        // create person + userLogin
+        Map<String, Serializable> createPersonUlMap = FastMap.newInstance();
+        String userLoginId = delegator.getNextSeqId("UserLogin");
+        if (userInfo.containsKey("firstName")) {
+            createPersonUlMap.put("firstName", userInfo.get("firstName"));
+        }
+        if (userInfo.containsKey("lastName")) {
+            createPersonUlMap.put("lastName", userInfo.get("lastName"));
+        }
+        if (userInfo.containsKey("userId")) {
+            createPersonUlMap.put("externalAuthId", userInfo.get("userId"));
+        }
+        // createPersonUlMap.put("externalId", user.getUserId());
+        createPersonUlMap.put("userLoginId", userLoginId);
+        createPersonUlMap.put("currentPassword", "[EXTERNAL]");
+        createPersonUlMap.put("currentPasswordVerify", "[EXTERNAL]");
+        createPersonUlMap.put("userLogin", system);
+        Map<String, Object> createPersonResult;
+        try {
+            createPersonResult = dispatcher.runSync("createPersonAndUserLogin", createPersonUlMap);
+        } catch (GenericServiceException e) {
+            throw new AuthenticatorException(e.getMessage(), e);
+        }
+        if (ServiceUtil.isError(createPersonResult)) {
+            throw new AuthenticatorException(ServiceUtil.getErrorMessage(createPersonResult));
+        }
+        String partyId = (String) createPersonResult.get("partyId");
+
+        // give this person a role of CUSTOMER
+        GenericValue partyRole = delegator.makeValue("PartyRole", UtilMisc.toMap("partyId", partyId, "roleTypeId", "CUSTOMER"));
+        try {
+            delegator.create(partyRole);
+        } catch (GenericEntityException e) {
+            Debug.logError(e, module);
+            throw new AuthenticatorException(e.getMessage(), e);
+        }
+
+        // create email
+        if (userInfo.containsKey("emailAddress")) {
+            Map<String, Serializable> createEmailMap = FastMap.newInstance();
+            createEmailMap.put("emailAddress", userInfo.get("emailAddress"));
+            createEmailMap.put("contactMechPurposeTypeId", "PRIMARY_EMAIL");
+            createEmailMap.put("partyId", partyId);
+            createEmailMap.put("userLogin", system);
+            Map<String, Object> createEmailResult;
+            try {
+                createEmailResult = dispatcher.runSync("createPartyEmailAddress", createEmailMap);
+            } catch (GenericServiceException e) {
+                throw new AuthenticatorException(e.getMessage(), e);
+            }
+            if (ServiceUtil.isError(createEmailResult)) {
+                throw new AuthenticatorException(ServiceUtil.getErrorMessage(createEmailResult));
+            }
+        }
+
+        // create security group(s)
+        Timestamp now = UtilDateTime.nowTimestamp();
+        for (String securityGroup : (new LinkedInUserGroupMapper(new String[] {"person"}).getSecurityGroups())) {
+            // check and make sure the security group exists
+            GenericValue secGroup = null;
+            try {
+                secGroup = delegator.findOne("SecurityGroup", UtilMisc.toMap("groupId", securityGroup), true);
+            } catch (GenericEntityException e) {
+                Debug.logError(e, e.getMessage(), module);
+            }
+
+            // add it to the user if it exists
+            if (secGroup != null) {
+                Map<String, Serializable> createSecGrpMap = FastMap.newInstance();
+                createSecGrpMap.put("userLoginId", userLoginId);
+                createSecGrpMap.put("groupId", securityGroup);
+                createSecGrpMap.put("fromDate", now);
+                createSecGrpMap.put("userLogin", system);
+
+                Map<String, Object> createSecGrpResult;
+                try {
+                    createSecGrpResult = dispatcher.runSync("addUserLoginToSecurityGroup", createSecGrpMap);
+                } catch (GenericServiceException e) {
+                    throw new AuthenticatorException(e.getMessage(), e);
+                }
+                if (ServiceUtil.isError(createSecGrpResult)) {
+                    throw new AuthenticatorException(ServiceUtil.getErrorMessage(createSecGrpResult));
+                }
+            }
+        }
+        return userLoginId;
+    }
+
+    private void updateUser(Document user, GenericValue system, GenericValue userLogin) throws AuthenticatorException {
+        // TODO implement me
+    }
+
+    /**
+     * Updates a user's password.
+     *
+     * @param username    User's username
+     * @param password    User's current password
+     * @param newPassword User's new password
+     * @throws org.ofbiz.common.authentication.api.AuthenticatorException
+     *          when update password fails
+     */
+    public void updatePassword(String username, String password, String newPassword) throws AuthenticatorException {
+        Debug.logInfo("Calling LinkedIn:updatePassword() - ignored!!!", module);
+    }
+
+    /**
+     * Weight of this authenticator (lower weights are run first)
+     *
+     * @return the weight of this Authenicator
+     */
+    public float getWeight() {
+        return 1;
+    }
+
+    /**
+     * Is the user synchronzied back to OFBiz
+     *
+     * @return true if the user record is copied to the OFB database
+     */
+    public boolean isUserSynchronized() {
+        return true;
+    }
+
+    /**
+     * Is this expected to be the only authenticator, if so errors will be thrown when users cannot be found
+     *
+     * @return true if this is expected to be the only Authenticator
+     */
+    public boolean isSingleAuthenticator() {
+        return false;
+    }
+
+    /**
+     * Flag to test if this Authenticator is enabled
+     *
+     * @return true if the Authenticator is enabled
+     */
+    public boolean isEnabled() {
+        return "true".equalsIgnoreCase(UtilProperties.getPropertyValue(props, "linked.authenticator.enabled", "true"));
+    }
+
+    public static Document getUserInfo(GetMethod getMethod, Locale locale) throws HttpException, IOException, AuthenticatorException, SAXException, ParserConfigurationException {
+        Document userInfo = null;
+        HttpClient jsonClient = new HttpClient();
+        HttpMethodParams params = new HttpMethodParams();
+        params.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
+        getMethod.setParams(params);
+        jsonClient.executeMethod(getMethod);
+        if (getMethod.getStatusCode() == HttpStatus.SC_OK) {
+            Debug.logInfo("Json Response from LinkedIn: " + getMethod.getResponseBodyAsString(), module);
+            userInfo = UtilXml.readXmlDocument(getMethod.getResponseBodyAsString());
+        } else {
+            String errMsg = UtilProperties.getMessage(resource, "GetOAuth2AccessTokenError", UtilMisc.toMap("error", getMethod.getResponseBodyAsString()), locale);
+            throw new AuthenticatorException(errMsg);
+        }
+        return userInfo;
+    }
+
+    public static String getLinkedInUserId(Document userInfo) {
+        NodeList persons = userInfo.getElementsByTagName("person");
+        if (UtilValidate.isEmpty(persons) || persons.getLength() <= 0) {
+            return null;
+        }
+        Element standardProfileRequest = UtilXml.firstChildElement((Element) persons.item(0), "site-standard-profile-request");
+        Element url = UtilXml.firstChildElement(standardProfileRequest, "url");
+        if (UtilValidate.isNotEmpty(url)) {
+            String urlContent = url.getTextContent();
+            if (UtilValidate.isNotEmpty(urlContent)) {
+                String id = urlContent.substring(urlContent.indexOf("?id="));
+                id = id.substring(0, id.indexOf("&"));
+                Debug.logInfo("LinkedIn user id: " + id, module);
+                return id;
+            }
+        }
+        return null;
+    }
+
+    public static Map<String, String> parseLinkedInUserInfo(Document userInfo) {
+        Map<String, String> results = FastMap.newInstance();
+        NodeList persons = userInfo.getElementsByTagName("person");
+        if (UtilValidate.isEmpty(persons) || persons.getLength() <= 0) {
+            return results;
+        }
+        Element person = (Element) persons.item(0);
+        Element standardProfileRequest = UtilXml.firstChildElement(person, "site-standard-profile-request");
+        Element url = UtilXml.firstChildElement(standardProfileRequest, "url");
+        if (UtilValidate.isNotEmpty(url)) {
+            String urlContent = url.getTextContent();
+            if (UtilValidate.isNotEmpty(urlContent)) {
+                String id = urlContent.substring(urlContent.indexOf("?id="));
+                id = id.substring(0, id.indexOf("&"));
+                Debug.logInfo("LinkedIn user id: " + id, module);
+                results.put("userId", id);
+            }
+        }
+        Element firstNameElement = UtilXml.firstChildElement(person, "first-name");
+        if (UtilValidate.isNotEmpty(firstNameElement) && UtilValidate.isNotEmpty(firstNameElement.getTextContent())) {
+            results.put("firstName", firstNameElement.getTextContent());
+        }
+        Element lastNameElement = UtilXml.firstChildElement(person, "last-name");
+        if (UtilValidate.isNotEmpty(lastNameElement) && UtilValidate.isNotEmpty(lastNameElement.getTextContent())) {
+            results.put("lastName", lastNameElement.getTextContent());
+        }
+        Element emailElement = UtilXml.firstChildElement(person, "email-address");
+        if (UtilValidate.isNotEmpty(emailElement) && UtilValidate.isNotEmpty(emailElement.getTextContent())) {
+            results.put("emailAddress", emailElement.getTextContent());
+        }
+        return results;
+    }
+}

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInAuthenticator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInAuthenticator.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInAuthenticator.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInUserGroupMapper.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInUserGroupMapper.java?rev=1688503&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInUserGroupMapper.java (added)
+++ ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInUserGroupMapper.java Tue Jun 30 18:38:16 2015
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.ofbiz.passport.user;
+
+import java.util.List;
+import java.util.Properties;
+import java.util.Arrays;
+import java.util.Set;
+
+import org.ofbiz.base.util.UtilProperties;
+
+import javolution.util.FastList;
+import javolution.util.FastSet;
+
+/**
+ * LinkedIn UserGroupMapper
+ */
+public class LinkedInUserGroupMapper {
+
+    protected List<String> groups;
+
+    public LinkedInUserGroupMapper(String[] groups) {
+        this.groups = Arrays.asList(groups);
+    }
+
+    public LinkedInUserGroupMapper(String group) {
+        if (groups == null) {
+            groups = FastList.newInstance();
+        }
+        groups.add(group);
+    }
+
+    public Set<String> getSecurityGroups() {
+        Properties props = UtilProperties.getProperties(LinkedInAuthenticator.props);
+
+        Set<String> secGroups = FastSet.newInstance();
+        boolean running = true;
+        int index = 1;
+
+        while (running) {
+            String groupStr = (String) props.get("linkedin.group.map." + index);
+            if (groupStr == null) {
+                running = false;
+            } else {
+                String[] groupSplit = groupStr.split("=");
+                if (groupSplit.length == 2) {
+                    if (groups.contains(groupSplit[0])) {
+                        secGroups.add(groupSplit[1]);
+                    }
+                }
+            }
+            index++;
+        }
+        return secGroups;
+    }
+}

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInUserGroupMapper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInUserGroupMapper.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/user/LinkedInUserGroupMapper.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/util/PassportUtil.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/util/PassportUtil.java?rev=1688503&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/util/PassportUtil.java (added)
+++ ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/util/PassportUtil.java Tue Jun 30 18:38:16 2015
@@ -0,0 +1,196 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.ofbiz.passport.util;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLContextBuilder;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.ofbiz.base.util.Debug;
+
+public class PassportUtil {
+
+    public static final String module = PassportUtil.class.getName();
+    
+    public static final String ClientIdLabel = "ClientId";
+    
+    public static final String SecretLabel = "Secret";
+
+    public static final String ReturnUrlLabel = "ReturnUrl";
+
+    public static final String TokenEndpointLabel = "TokenEndpoint";
+
+    public static final String GrantTypeLabel = "grantType";
+
+    public static final String ContentTypeLabel = "contentType";
+
+    public static final String AUTHORIZATION_HEADER = "Authorization";
+
+    public static final String UserProfileUrlLabel = "UserProfileUrl";
+
+    public static final String GrantTypeParam = "grant_type";
+
+    public static final String ContentTypeParam = "Content-Type";
+
+    public static final String ACCEPT_HEADER = "Accept";
+
+    public static final String APPLICATION_JSON = "application/json";
+
+    public static final String RESTApiEndpointLabel = "RESTApiEndpoint";
+
+    public static final String COMMON_CODE = "code";
+
+    public static final String COMMON_SCOPE = "scope";
+
+    public static final String AuthorizationCodeGrantType = "authorization_code";
+
+    public static final String COMMON_STATE = "state";
+
+    public static final String COMMON_ERROR = "error";
+
+    public static final String COMMON_ERROR_DESCRIPTION = "error_description";
+
+    public static final String ApiKeyLabel = "apiKey";
+
+    public static final String SecretKeyLabel = "secretKey";
+
+    public static final String COMMON_CLIENT_ID = "clientId";
+
+    public static final String COMMON_RETURN_RUL = "returnUrl";
+
+    public static final String COMMON_CLIENT_SECRET = "clientSecret";
+
+    public static final String ApiIdLabel = "apiId";
+
+    public static final String AppKeyLabel = "appKey";
+
+    public static final String AppSecretLabel = "appSecret";
+
+    public static final String AppIdLabel = "appId";
+
+    public static final String COMMON_APP_KEY = "AppKey";
+
+    public static final String COMMON_APP_SECRET = "AppSecret";
+    
+    protected PassportUtil() {
+        // empty constructor
+    }
+    
+    public static PassportUtil getInstance() {
+        return new PassportUtil();
+    }
+
+    public static String getEnvPrefixByHost(HttpServletRequest request) {
+        String prefix ="test";
+        try {
+            InetAddress[] addresses = InetAddress.getAllByName(request.getServerName());
+            for (InetAddress address : addresses) {
+                if (address.isAnyLocalAddress() || address.isLinkLocalAddress() || address.isLoopbackAddress()) {
+                    return prefix;
+                }
+            }
+            prefix = "live";
+        } catch (UnknownHostException e) {
+            Debug.logError(e.getMessage(), module);
+        }
+        return prefix;
+    }
+
+    private static String randomString(int lo, int hi) {
+        int n = rand(lo, hi);
+        byte b[] = new byte[n];
+        for (int i = 0; i < n; i++) {
+            b[i] = (byte)rand('a', 'z');
+        }
+        return new String(b);
+    }
+
+    private static int rand(int lo, int hi) {
+        java.util.Random rn = new java.util.Random();
+        int n = hi - lo + 1;
+        int i = rn.nextInt() % n;
+        if (i < 0)
+                i = -i;
+        return lo + i;
+    }
+
+    public static String randomString() {
+        return randomString(8, 15);
+    }
+
+    public CloseableHttpClient getAllowAllHttpClient() {
+        try {
+            SSLContextBuilder builder = new SSLContextBuilder();
+            builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
+            SSLConnectionSocketFactory sf = new AllowAllSSLSocketFactory(builder.build());
+            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sf).build();
+            return httpclient;
+        } catch (Exception e) {
+            return HttpClients.createDefault();
+        }
+    }
+
+    public class AllowAllSSLSocketFactory extends SSLConnectionSocketFactory {
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+
+        public AllowAllSSLSocketFactory(SSLContext sslContext) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+            super(sslContext);
+
+            TrustManager tm = new X509TrustManager() {
+                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                }
+
+                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                }
+
+                public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+            };
+
+            sslContext.init(null, new TrustManager[] { tm }, null);
+        }
+
+        public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
+            return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
+        }
+
+        public Socket createSocket() throws IOException {
+            return sslContext.getSocketFactory().createSocket();
+        }
+    }
+
+}
\ No newline at end of file

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/util/PassportUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/util/PassportUtil.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/trunk/specialpurpose/passport/src/org/ofbiz/passport/util/PassportUtil.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/actions/login/getThirdPartyLogins.groovy
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/actions/login/getThirdPartyLogins.groovy?rev=1688503&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/actions/login/getThirdPartyLogins.groovy (added)
+++ ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/actions/login/getThirdPartyLogins.groovy Tue Jun 30 18:38:16 2015
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.ofbiz.passport;
+
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.util.EntityUtil;
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.product.store.ProductStoreWorker;
+
+final String module = "getThirdPartyLogins.groovy"
+
+adminErrorInfo = context.adminErrorInfo;
+productStoreId = context.productStoreId;
+if (!productStoreId) {
+    productStore = ProductStoreWorker.getProductStore(request);
+    productStoreId = productStore.productStoreId;
+}
+
+if (!adminErrorInfo || !adminErrorInfo.hasError()) {
+    storePassportLoginMethList = null;
+    // Get lists of passport login methods
+    if (productStoreId) {
+        storePassportLoginMethList = delegator.findByAnd("ThirdPartyLogin", ["productStoreId": productStoreId], ["sequenceNum ASC"], false);
+        storePassportLoginMethList = EntityUtil.filterByDate(storePassportLoginMethList);
+    }
+        
+    // Extra data preparation for each login method.
+    if (storePassportLoginMethList) {
+        storeLoginMethList = []
+        for (storeLoginMeth in storePassportLoginMethList) {
+            storeLoginMethDetail = EntityUtil.getFirst(EntityUtil.filterByDate(delegator.findByAnd(storeLoginMeth.loginMethTypeId + storeLoginMeth.loginProviderId, ["productStoreId": productStoreId], null, false)));
+            storeLoginMethList.add(storeLoginMethDetail)
+        }
+        context.storeLoginMethList = storeLoginMethList
+    }
+}

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/actions/login/getThirdPartyLogins.groovy
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/actions/login/getThirdPartyLogins.groovy
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/actions/login/getThirdPartyLogins.groovy
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/controller-passport.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/controller-passport.xml?rev=1688503&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/controller-passport.xml (added)
+++ ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/controller-passport.xml Tue Jun 30 18:38:16 2015
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<site-conf xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/site-conf.xsd">
+
+    <!-- GitHub requests -->
+    <request-map uri="gitHubRedirect">
+        <security https="true" auth="false"/>
+        <event type="java" path="org.ofbiz.passport.event.GitHubEvents" invoke="gitHubRedirect"/>
+        <response name="success" type="view" value="main"/>
+        <response name="error" type="view" value="login"/>
+    </request-map>
+    
+    <request-map uri="githubResponse">
+        <security https="true" auth="false"/>
+        <event type="java" path="org.ofbiz.passport.event.GitHubEvents" invoke="parseGitHubResponse"/>
+        <response name="success" type="request" value="login">
+            <redirect-parameter name="USERNAME"/>
+            <redirect-parameter name="PASSWORD"/>
+        </response>
+        <response name="error" type="view" value="login"/>
+    </request-map>
+    
+    <!-- LinkedIn requests -->
+    <request-map uri="linkedInRedirect">
+        <security https="true" auth="false"/>
+        <event type="java" path="org.ofbiz.passport.event.LinkedInEvents" invoke="linkedInRedirect"/>
+        <response name="success" type="view" value="main"/>
+        <response name="error" type="view" value="login"/>
+    </request-map>
+    
+    <request-map uri="linkedInResponse">
+        <security https="true" auth="false"/>
+        <event type="java" path="org.ofbiz.passport.event.LinkedInEvents" invoke="parseLinkedInResponse"/>
+        <response name="success" type="request" value="login">
+            <redirect-parameter name="USERNAME"/>
+            <redirect-parameter name="PASSWORD"/>
+        </response>
+        <response name="error" type="view" value="login"/>
+    </request-map>
+    
+</site-conf>

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/controller-passport.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/controller-passport.xml
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/WEB-INF/controller-passport.xml
------------------------------------------------------------------------------
    svn:mime-type = text/xml

Added: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/alipay.png
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/webapp/passport/images/alipay.png?rev=1688503&view=auto
==============================================================================
Binary file - no diff available.

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/alipay.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/github.png
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/webapp/passport/images/github.png?rev=1688503&view=auto
==============================================================================
Binary file - no diff available.

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/github.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/linkedin.png
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/webapp/passport/images/linkedin.png?rev=1688503&view=auto
==============================================================================
Binary file - no diff available.

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/linkedin.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/paypal.png
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/webapp/passport/images/paypal.png?rev=1688503&view=auto
==============================================================================
Binary file - no diff available.

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/paypal.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/qq.png
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/webapp/passport/images/qq.png?rev=1688503&view=auto
==============================================================================
Binary file - no diff available.

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/qq.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/renren.png
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/webapp/passport/images/renren.png?rev=1688503&view=auto
==============================================================================
Binary file - no diff available.

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/renren.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/taobao.png
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/webapp/passport/images/taobao.png?rev=1688503&view=auto
==============================================================================
Binary file - no diff available.

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/taobao.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/weibo.png
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/webapp/passport/images/weibo.png?rev=1688503&view=auto
==============================================================================
Binary file - no diff available.

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/weibo.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/weixin.png
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/webapp/passport/images/weixin.png?rev=1688503&view=auto
==============================================================================
Binary file - no diff available.

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/images/weixin.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: ofbiz/trunk/specialpurpose/passport/webapp/passport/login/thirdPartyLogins.ftl
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/webapp/passport/login/thirdPartyLogins.ftl?rev=1688503&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/passport/webapp/passport/login/thirdPartyLogins.ftl (added)
+++ ofbiz/trunk/specialpurpose/passport/webapp/passport/login/thirdPartyLogins.ftl Tue Jun 30 18:38:16 2015
@@ -0,0 +1,29 @@
+<#--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<#if storeLoginMethList?exists && storeLoginMethList?has_content>
+<div class="screenlet">
+  <div class="screenlet-title-bar"><h3>${uiLabelMap.ThirdPartyLogin}</h3></div>
+  <div class="screenlet-body">
+    <#list storeLoginMethList as storeLoginMeth>
+      <span><a href="${storeLoginMeth.localRedirectUri!}"><img src="${storeLoginMeth.iconUrl!}"></a></span>
+    </#list>
+  </div>
+</div>
+</#if>

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/login/thirdPartyLogins.ftl
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/login/thirdPartyLogins.ftl
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/trunk/specialpurpose/passport/webapp/passport/login/thirdPartyLogins.ftl
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/trunk/specialpurpose/passport/widget/PassportScreens.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/passport/widget/PassportScreens.xml?rev=1688503&view=auto
==============================================================================
--- ofbiz/trunk/specialpurpose/passport/widget/PassportScreens.xml (added)
+++ ofbiz/trunk/specialpurpose/passport/widget/PassportScreens.xml Tue Jun 30 18:38:16 2015
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<screens xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/widget-screen.xsd">
+   
+    
+    <!-- ++++++ Third Party Logins ++++++ -->
+     <screen name="ListThirdPartyLogins">
+        <section>
+            <actions> 
+                <property-map resource="PassportUiLabels" map-name="uiLabelMap" global="true"/> 
+                  <script location="component://passport/webapp/passport/WEB-INF/actions/login/getThirdPartyLogins.groovy"/>         
+            </actions>
+            <widgets>
+                <platform-specific>
+                    <html>
+                       <html-template location="component://passport/webapp/passport/login/thirdPartyLogins.ftl"/>
+                    </html>
+                </platform-specific>
+            </widgets>
+        </section>
+    </screen>
+    
+</screens>
\ No newline at end of file

Propchange: ofbiz/trunk/specialpurpose/passport/widget/PassportScreens.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/specialpurpose/passport/widget/PassportScreens.xml
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/trunk/specialpurpose/passport/widget/PassportScreens.xml
------------------------------------------------------------------------------
    svn:mime-type = text/xml