/** * Copyright 2013 Tommi S.E. Laukkanen * * Licensed 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.bubblecloud.ilves.security; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.apache.oltu.oauth2.client.OAuthClient; import org.apache.oltu.oauth2.client.URLConnectionClient; import org.apache.oltu.oauth2.client.request.OAuthClientRequest; import org.apache.oltu.oauth2.client.response.GitHubTokenResponse; import org.apache.oltu.oauth2.common.OAuthProviderType; import org.apache.oltu.oauth2.common.message.types.GrantType; import org.bubblecloud.ilves.model.Company; import org.bubblecloud.ilves.model.Customer; import org.bubblecloud.ilves.model.PostalAddress; import org.bubblecloud.ilves.model.User; import org.bubblecloud.ilves.module.customer.CustomerModule; import org.bubblecloud.ilves.site.SiteContext; import org.bubblecloud.ilves.site.SiteModuleManager; import javax.persistence.EntityManager; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Date; import java.util.Map; /** * Service for OAuth. * * @author Tommi S.E Laukkanen */ public class OAuthService { /** The logger. */ private static final Logger LOGGER = Logger.getLogger(OAuthService.class); public static String requestOAuthLocationUri(final SiteContext context) { try { final Company company = context.getObject(Company.class); if (!company.isoAuthLogin()) { return null; } OAuthClientRequest request = OAuthClientRequest .authorizationProvider(OAuthProviderType.GITHUB) .setClientId(company.getGitHubClientId()) .setRedirectURI(company.getUrl() + "oauthredirect") .setScope("user:email") .buildQueryMessage(); return request.getLocationUri(); } catch (final Exception e) { LOGGER.error("Error in oauth.", e); return null; } } public static User processOAuthRedirect(final SiteContext context, final Company company, String code) { if (!company.isoAuthLogin()) { return null; } final EntityManager entityManager = context.getEntityManager(); if (StringUtils.isEmpty(code)) { LOGGER.warn("Warning in oauth no code received in redirect."); return null; } try { final OAuthClientRequest oAuthClientRequest = OAuthClientRequest .tokenProvider(OAuthProviderType.GITHUB) .setGrantType(GrantType.AUTHORIZATION_CODE) .setClientId(company.getGitHubClientId()) .setClientSecret(company.getGitHubClientSecret()) .setRedirectURI(company.getUrl() + "oauthredirect") .setCode(code) .buildQueryMessage(); final OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient()); final GitHubTokenResponse oAuthResponse = oAuthClient.accessToken(oAuthClientRequest, GitHubTokenResponse.class); final String accessToken = oAuthResponse.getAccessToken(); final String primaryVerifiedEmail = getEmail(accessToken); if (primaryVerifiedEmail == null) { AuditService.log(context, "oauth login failed, no matching email"); return null; } final User existingUser = UserDao.getUser(entityManager, company, primaryVerifiedEmail); if (existingUser != null) { if (existingUser.isLockedOut()) { AuditService.log(context, "oauth login failed, locked user", "User", existingUser.getUserId(), existingUser.getEmailAddress()); return null; } AuditService.log(context, "oauth login success", "User", existingUser.getUserId(), existingUser.getEmailAddress()); return existingUser; } else { if (!company.isoAuthSelfRegistration()) { return null; } final String name = primaryVerifiedEmail.split("@")[0]; final String[] nameParts = name.split("\\."); final String firstName = capitalizeFirstLetter(nameParts[0]); final String lastName = nameParts.length > 1 ? capitalizeFirstLetter(nameParts[nameParts.length - 1]) : "-"; final String phoneNumber = "-"; final User newUser = new User(company, firstName, lastName, primaryVerifiedEmail, phoneNumber, ""); UserDao.addUser(entityManager, newUser, UserDao.getGroup(entityManager, company, "user")); if (SiteModuleManager.isModuleInitialized(CustomerModule.class)) { final Customer customer = new Customer(firstName, lastName, primaryVerifiedEmail, phoneNumber, false, "", ""); customer.setCreated(new Date()); customer.setModified(customer.getCreated()); customer.setOwner(company); final PostalAddress invoicingAddress = new PostalAddress(); invoicingAddress.setAddressLineOne("-"); invoicingAddress.setAddressLineTwo("-"); invoicingAddress.setAddressLineThree("-"); invoicingAddress.setCity("-"); invoicingAddress.setPostalCode("-"); invoicingAddress.setCountry("-"); final PostalAddress deliveryAddress = new PostalAddress(); deliveryAddress.setAddressLineOne("-"); deliveryAddress.setAddressLineTwo("-"); deliveryAddress.setAddressLineThree("-"); deliveryAddress.setCity("-"); deliveryAddress.setPostalCode("-"); deliveryAddress.setCountry("-"); customer.setInvoicingAddress(invoicingAddress); customer.setDeliveryAddress(deliveryAddress); CustomerDao.addCustomer(entityManager, customer); UserDao.addGroupMember(context.getEntityManager(), customer.getAdminGroup(), newUser); UserDao.addGroupMember(context.getEntityManager(), customer.getMemberGroup(), newUser); } AuditService.log(context, "oauth-auto-register", "user", newUser.getUserId(), primaryVerifiedEmail); return newUser; } } catch (final Exception e) { LOGGER.error("Error exchanging oauth code to access token: " + e.getMessage()); AuditService.log(context, "oauth login exception"); return null; } } public static String capitalizeFirstLetter(String original){ if(original.length() == 0) return original; return original.substring(0, 1).toUpperCase() + original.substring(1); } public static String getEmail(String accessToken) throws Exception { final String response = get("https://api.github.com/user/emails?access_token", accessToken); ObjectMapper objectMapper = new ObjectMapper(); final ArrayList<Map<String, Object>> emailList = objectMapper.readValue(response, ArrayList.class); String primaryVerifiedEmail = null; for (final Map email : emailList) { if (email.containsKey("email") && email.containsKey("verified") && email.containsKey("primary")) { if ((Boolean) email.get("verified") && (Boolean) email.get("primary")) { primaryVerifiedEmail = (String) email.get("email"); } } } return primaryVerifiedEmail; } public static String get(final String url, final String accessToken) throws Exception { final HttpURLConnection httpURLConnection = (HttpURLConnection) new URL(url).openConnection(); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setRequestProperty("User-Agent", "Mozilla/5.0"); httpURLConnection.setRequestProperty("Authorization", "token " + accessToken); return IOUtils.toString(httpURLConnection.getInputStream()); } }