content: //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
  //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.apache.cloudstack.oauth2.google;
  
  import com.cloud.exception.CloudAuthenticationException;
  import com.cloud.utils.component.AdapterBase;
  import com.cloud.utils.exception.CloudRuntimeException;
  import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
  import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
  import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
  import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
  import com.google.api.client.http.javanet.NetHttpTransport;
  import com.google.api.client.json.JsonFactory;
  import com.google.api.client.json.jackson2.JacksonFactory;
  import com.google.api.services.oauth2.Oauth2;
  import com.google.api.services.oauth2.model.Userinfo;
  import org.apache.cloudstack.auth.UserOAuth2Authenticator;
  import org.apache.cloudstack.oauth2.dao.OauthProviderDao;
  import org.apache.cloudstack.oauth2.vo.OauthProviderVO;
  import org.apache.commons.lang3.StringUtils;
  
  import javax.inject.Inject;
  import java.io.IOException;
  import java.util.Arrays;
  import java.util.List;
  
  public class GoogleOAuth2Provider extends AdapterBase implements UserOAuth2Authenticator {
  
      protected String accessToken = null;
      protected String refreshToken = null;
  
      @Inject
      OauthProviderDao _oauthProviderDao;
  
      @Override
      public String getName() {
          return "google";
      }
  
      @Override
      public String getDescription() {
          return "Google OAuth2 Provider Plugin";
      }
  
      @Override
      public boolean verifyUser(String email, String secretCode) {
          if (StringUtils.isAnyEmpty(email, secretCode)) {
              throw new CloudAuthenticationException("Either email or secret code should not be null/empty");
          }
  
          OauthProviderVO providerVO = _oauthProviderDao.findByProvider(getName());
          if (providerVO == null) {
              throw new CloudAuthenticationException("Google provider is not registered, so user cannot be verified");
          }
  
          String verifiedEmail = verifyCodeAndFetchEmail(secretCode);
          if (verifiedEmail == null || !email.equals(verifiedEmail)) {
              throw new CloudRuntimeException("Unable to verify the email address with the provided secret");
          }
          clearAccessAndRefreshTokens();
  
          return true;
      }
  
      @Override
      public String verifyCodeAndFetchEmail(String secretCode) {
          OauthProviderVO githubProvider = _oauthProviderDao.findByProvider(getName());
          String clientId = githubProvider.getClientId();
          String secret = githubProvider.getSecretKey();
          String redirectURI = githubProvider.getRedirectUri();
          GoogleClientSecrets clientSecrets = new GoogleClientSecrets()
                  .setWeb(new GoogleClientSecrets.Details()
                          .setClientId(clientId)
                          .setClientSecret(secret));
  
          NetHttpTransport httpTransport = new NetHttpTransport();
          JsonFactory jsonFactory = new JacksonFactory();
          List<String> scopes = Arrays.asList(
                                  "https://www.googleapis.com/auth/userinfo.profile",
                                  "https://www.googleapis.com/auth/userinfo.email");
          GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
                  httpTransport, jsonFactory, clientSecrets, scopes)
                  .build();
  
          if (StringUtils.isAnyEmpty(accessToken, refreshToken)) {
              GoogleTokenResponse tokenResponse = null;
              try {
                  tokenResponse = flow.newTokenRequest(secretCode)
                          .setRedirectUri(redirectURI)
                          .execute();
              } catch (IOException e) {
                  throw new RuntimeException(e);
              }
              accessToken = tokenResponse.getAccessToken();
              refreshToken = tokenResponse.getRefreshToken();
          }
  
          GoogleCredential credential = new GoogleCredential.Builder()
                  .setTransport(httpTransport)
                  .setJsonFactory(jsonFactory)
                  .setClientSecrets(clientSecrets)
                  .build()
                  .setAccessToken(accessToken)
                  .setRefreshToken(refreshToken);
  
          Oauth2 oauth2 = new Oauth2.Builder(httpTransport, jsonFactory, credential).build();
          Userinfo userinfo = null;
          try {
              userinfo = oauth2.userinfo().get().execute();
          } catch (IOException e) {
              throw new CloudRuntimeException(String.format("Failed to fetch the email address with the provided secret: %s" + e.getMessage()));
          }
          return userinfo.getEmail();
      }
  
      protected void clearAccessAndRefreshTokens() {
          accessToken = null;
          refreshToken = null;
      }
  
      @Override
      public String getUserEmailAddress() throws CloudRuntimeException {
          return null;
      }
  }
  