<commit_before>package io.cattle.platform.iaas.api.auth.integration.azure;

import io.cattle.platform.api.auth.Identity;
import io.cattle.platform.json.JsonMapper;
import io.github.ibuildthecloud.gdapi.context.ApiContext;
import io.github.ibuildthecloud.gdapi.exception.ClientVisibleException;
import io.github.ibuildthecloud.gdapi.util.ResponseCodes;
import io.cattle.platform.util.type.CollectionUtils;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.fluent.Request;
import org.apache.http.entity.ContentType;

public class AzureRESTClient extends AzureConfigurable{

    @Inject
    private JsonMapper jsonMapper;
    
    @Inject
    private AzureTokenUtil azureTokenUtil;

    private static final Log logger = LogFactory.getLog(AzureRESTClient.class);

    private Identity getUserIdentity(String azureAccessToken) {
        if (StringUtils.isEmpty(azureAccessToken)) {
            noAccessToken();
        }
        try {
            logger.debug("getUserIdentity for logged in user");
            HttpResponse response = getFromAzure(azureAccessToken, getURL(AzureClientEndpoints.USER, ""));
            if(hasTokenExpired(response)) {
                //refresh token
                refreshAccessToken();
                //retry the request
                azureAccessToken = (String) ApiContext.getContext().getApiRequest().getAttribute(AzureConstants.AZURE_ACCESS_TOKEN);
                response = getFromAzure(azureAccessToken, getURL(AzureClientEndpoints.USER, ""));
            }
            
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode >= 300) {
                noAzure(response);
            }
            
            Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent());
            return jsonToAzureAccountInfo(jsonData).toIdentity(AzureConstants.USER_SCOPE);
        } catch (IOException e) {
            logger.error("Failed to get Azure user account info.", e);
            throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR, AzureConstants.AZURE_CLIENT,
                    "Failed to get Azure user account info.", null);
        } catch(ClientVisibleException ex) {
            logger.error("Failed to get Azure user account info.", ex);
            throw ex;
        } catch(Exception ex) {
            logger.error("Failed to get Azure user account info.", ex);
            throw new RuntimeException(ex);
        }
    }

    private List<Identity> getGroupIdentities(String azureAccessToken) {
        if (StringUtils.isEmpty(azureAccessToken)) {
            noAccessToken();
        }
        try {
            logger.debug("getGroupIdentities for logged in user");
            HttpResponse response = getFromAzure(azureAccessToken, getURL(AzureClientEndpoints.GROUP, ""));
            if(hasTokenExpired(response)) {
                //refresh token
                refreshAccessToken();
                //retry the request
                azureAccessToken = (String) ApiContext.getContext().getApiRequest().getAttribute(AzureConstants.AZURE_ACCESS_TOKEN);
                response = getFromAzure(azureAccessToken, getURL(AzureClientEndpoints.GROUP, ""));
            }
            
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode >= 300) {
                noAzure(response);
            }

            Map<String, Object> jsonData = CollectionUtils.toMap(jsonMapper.readValue(response.getEntity().getContent(), Map.class));
            List<AzureAccountInfo> searchResponseList = parseSearchResponseList(jsonData);

            List<Identity> groupIdentities = new ArrayList<Identity>();

            if(searchResponseList != null && !searchResponseList.isEmpty()){
                for(AzureAccountInfo userOrGroup : searchResponseList){
                    groupIdentities.add(userOrGroup.toIdentity(AzureConstants.GROUP_SCOPE));
                }
            }

            return groupIdentities;
        } catch (IOException e) {
            logger.error("Failed to get Azure group memberships.", e);
            throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR, AzureConstants.AZURE_CLIENT,
                    "Failed to get Azure group memberships.", null);
        } catch(ClientVisibleException ex) {
            logger.error("Failed to get Azure group memberships.", ex);
            throw ex;
        } catch(Exception ex) {
            logger.error("Failed to get Azure group memberships.", ex);
            throw new RuntimeException(ex);
        }
    }


    public String getAccessToken(String code) {
        if (!isConfigured()) {
            throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR, AzureConstants.CONFIG, "Azure Client and Tenant Id not configured", null);
        }
        
        logger.debug("getAccessToken from Azure");
    
        String[] split = code.split(":", 2);
        if (split.length != 2) {
            throw new ClientVisibleException(ResponseCodes.BAD_REQUEST, "MalformedCode");
        }
        String username = split[0];
        String domain = AzureConstants.AZURE_DOMAIN.get();
        //check if the username has a domain, if not then append the domain.
        if(domain != null && domain != "" && !username.endsWith("@"+domain)) {
            username = username + "@"+ domain;
        }
        
        //testuser1%40ZTAD.onmicrosoft.com&password=<PASSWORD>";

        String accessToken, refreshToken;
        try{   
            StringBuilder body = new StringBuilder("scope=openid&grant_type=password&resource=https%3A%2F%2Fgraph.windows.net");
            body.append("&client_id=");
            body.append(URLEncoder.encode(AzureConstants.AZURE_CLIENT_ID.get(), "UTF-8"));
            body.append("&username=");
            body.append(URLEncoder.encode(username, "UTF-8"));
            body.append("&password=");
            body.append(URLEncoder.encode(split[1], "UTF-8")); 
            
            HttpResponse response = Request.Post(AzureConstants.AUTHORITY)
                    .addHeader(AzureConstants.ACCEPT, AzureConstants.APPLICATION_FORM_URL_ENCODED)
                    .bodyString(body.toString(), ContentType.APPLICATION_FORM_URLENCODED).execute().returnResponse();
            int statusCode = response.getStatusLine().getStatusCode();
            if(statusCode >= 300) {
                noAzure(response);
            }

            Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent());
            accessToken = ObjectUtils.toString(jsonData.get("access_token"));
            refreshToken = ObjectUtils.toString(jsonData.get("refresh_token"));
            
            ApiContext.getContext().getApiRequest().setAttribute(AzureConstants.AZURE_ACCESS_TOKEN, accessToken);
            ApiContext.getContext().getApiRequest().setAttribute(AzureConstants.AZURE_REFRESH_TOKEN, refreshToken);

        } catch(ClientVisibleException ex) {
            throw ex;
        } catch(Exception ex) {
            throw new RuntimeException(ex);
        }

        return accessToken;

    }
    
    
    public void refreshAccessToken() {
        if (!isConfigured()) {
            throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR, AzureConstants.CONFIG, "Azure Client and Tenant Id not configured", null);
        }
        
        logger.debug("Azure accessToken expired, refreshing the token");
        
        String azureAccessToken = (String) ApiContext.getContext().getApiRequest().getAttribute(AzureConstants.AZURE_ACCESS_TOKEN);
        if (StringUtils.isEmpty(azureAccessToken)) {
            noAccessToken();
        }
        
        String azureRefreshToken = (String) ApiContext.getContext().getApiRequest().getAttribute(AzureConstants.AZURE_REFRESH_TOKEN);
        if (StringUtils.isEmpty(azureRefreshToken)) {
            noRefreshToken();
        }
        try{
            //post to the microsoft azure authority
            HttpResponse response = Request.Post(AzureConstants.AUTHORITY)
                    .addHeader(AzureConstants.ACCEPT, AzureConstants.APPLICATION_FORM_URL_ENCODED)
                    .bodyString("grant_type=refresh_token&refresh_token="+azureRefreshToken, ContentType.APPLICATION_FORM_URLENCODED)
                    .execute().returnResponse();
            int statusCode = response.getStatusLine().getStatusCode();
            if(statusCode >= 300) {
                noAzure(response);
            }
            
            Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent());
            
            String newAccessToken = ObjectUtils.toString(jsonData.get("access_token"));
            String newRefreshToken = ObjectUtils.toString(jsonData.get("refresh_token"));
            ApiContext.getContext().getApiRequest().setAttribute(AzureConstants.AZURE_ACCESS_TOKEN, newAccessToken);
            ApiContext.getContext().getApiRequest().setAttribute(AzureConstants.AZURE_REFRESH_TOKEN, newRefreshToken);
            logger.debug("Storing the new Azure access token to the Account");
            azureTokenUtil.refreshAccessToken();
    
        } catch(ClientVisibleException ex) {
            logger.error("Failed to refreshAccessToken for Azure user.", ex);
            throw ex;
        } catch(Exception ex) {
            logger.error("Failed to refreshAccessToken for Azure user.", ex);
            throw new RuntimeException(ex);
        }

    }

    public List<AzureAccountInfo> parseSearchResponseList(Map<String, Object> jsonData) {
        List<Object> azureValueList = (List<Object>)CollectionUtils.toList(jsonData.get("value"));
        List<AzureAccountInfo> azureUserOrGroupList = new ArrayList<AzureAccountInfo>();
        if (azureValueList != null && !azureValueList.isEmpty())
        {
            for(Object azureValue : azureValueList) {
                Map<String, Object> result = CollectionUtils.toMap(azureValue);
                String objectType = ObjectUtils.toString(result.get("objectType"));
                if(objectType != null && (objectType.equalsIgnoreCase("User") || objectType.equalsIgnoreCase("Group"))) {
                    AzureAccountInfo userOrGroupInfo = jsonToAzureAccountInfo(result);
                    azureUserOrGroupList.add(userOrGroupInfo);
                }
            }
        }
        return azureUserOrGroupList;
    }

    public AzureAccountInfo jsonToAzureAccountInfo(Map<String, Object> jsonData) {
        String objectId = ObjectUtils.toString(jsonData.get("objectId"));
        String userPrincipalName = ObjectUtils.toString(jsonData.get("userPrincipalName"));
        String accountName = ObjectUtils.toString(jsonData.get("mailNickname"));
        String name = ObjectUtils.toString(jsonData.get("displayName"));
        if (StringUtils.isBlank(name)) {
            name = accountName;
        }
        //String profilePicture = ObjectUtils.toString(jsonData.get(GithubConstants.PROFILE_PICTURE));
        return new AzureAccountInfo(objectId, accountName, null, userPrincipalName, name);
    }
    
    public boolean hasTokenExpired(HttpResponse response) throws IOException {
        int statusCode = response.getStatusLine().getStatusCode();
        if(statusCode == 401) {
            //if token expired, refresh token.
            Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent());
            Map<String, Object> azureError = CollectionUtils.toMap(jsonData.get("odata.error"));
            String azureCode = ObjectUtils.toString(azureError.get("code"));
            if("Authentication_ExpiredToken".equalsIgnoreCase(azureCode)) {
                return true;
            }
         }
        return false;
    }

    public HttpResponse getFromAzure(String azureAccessToken, String url) throws IOException {

        HttpResponse response = Request.Get(url).addHeader(AzureConstants.AUTHORIZATION, "Bearer " +
                "" + azureAccessToken).addHeader(AzureConstants.ACCEPT, AzureConstants.APPLICATION_JSON).execute().returnResponse();
        
        logger.debug("Response from Azure API: "+ response.getStatusLine());

        return response;
    }    

    private void noAccessToken() {
        throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR,
                "AzureAccessToken", "No Azure Access token", null);
    }
    
    private void noRefreshToken() {
        throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR,
                "AzureRefreshToken", "No Azure Refresh token", null);
    }
    
    public void noAzure(HttpResponse response) {
        int statusCode = response.getStatusLine().getStatusCode();

        try {
            Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent());
            String azureError = (String)jsonData.get("error");
            String azureErrorDesc = (String)jsonData.get("error_description");
            if("invalid_grant".equalsIgnoreCase(azureError) || "unauthorized_client".equalsIgnoreCase(azureError)) {
                throw new ClientVisibleException(ResponseCodes.UNAUTHORIZED);
            }
            throw new ClientVisibleException(ResponseCodes.SERVICE_UNAVAILABLE, AzureConstants.AZURE_ERROR,
                    "Error from Azure: " + Integer.toString(statusCode), "Details: " +azureError + ", Description: "+azureErrorDesc);
        } catch(ClientVisibleException ex) {
            logger.error("Got error from Azure.", ex);
            throw ex;
        } catch(Exception ex) {
            throw new ClientVisibleException(ResponseCodes.SERVICE_UNAVAILABLE, AzureConstants.AZURE_ERROR,
                    "Error Response from Azure", "Status code from Azure: " + Integer.toString(statusCode));
        } 
    }

    public String getURL(AzureClientEndpoints val, String objectId) {
        String apiEndpoint = AzureConstants.GRAPH_API_ENDPOINT;
        String tenantId = AzureConstants.AZURE_TENANT_ID.get();

        String toReturn;
        switch (val) {
            case USERS:
                toReturn = apiEndpoint + tenantId + "/users/" + objectId;
                break;<commit_msg>For Azure Group, use the displayName property for the accountName field since the mailNickname property is a uuid.<commit_after>package io.cattle.platform.iaas.api.auth.integration.azure;

import io.cattle.platform.api.auth.Identity;
import io.cattle.platform.json.JsonMapper;
import io.github.ibuildthecloud.gdapi.context.ApiContext;
import io.github.ibuildthecloud.gdapi.exception.ClientVisibleException;
import io.github.ibuildthecloud.gdapi.util.ResponseCodes;
import io.cattle.platform.util.type.CollectionUtils;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.fluent.Request;
import org.apache.http.entity.ContentType;

public class AzureRESTClient extends AzureConfigurable{

    @Inject
    private JsonMapper jsonMapper;
    
    @Inject
    private AzureTokenUtil azureTokenUtil;

    private static final Log logger = LogFactory.getLog(AzureRESTClient.class);

    private Identity getUserIdentity(String azureAccessToken) {
        if (StringUtils.isEmpty(azureAccessToken)) {
            noAccessToken();
        }
        try {
            logger.debug("getUserIdentity for logged in user");
            HttpResponse response = getFromAzure(azureAccessToken, getURL(AzureClientEndpoints.USER, ""));
            if(hasTokenExpired(response)) {
                //refresh token
                refreshAccessToken();
                //retry the request
                azureAccessToken = (String) ApiContext.getContext().getApiRequest().getAttribute(AzureConstants.AZURE_ACCESS_TOKEN);
                response = getFromAzure(azureAccessToken, getURL(AzureClientEndpoints.USER, ""));
            }
            
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode >= 300) {
                noAzure(response);
            }
            
            Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent());
            return jsonToAzureAccountInfo(jsonData).toIdentity(AzureConstants.USER_SCOPE);
        } catch (IOException e) {
            logger.error("Failed to get Azure user account info.", e);
            throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR, AzureConstants.AZURE_CLIENT,
                    "Failed to get Azure user account info.", null);
        } catch(ClientVisibleException ex) {
            logger.error("Failed to get Azure user account info.", ex);
            throw ex;
        } catch(Exception ex) {
            logger.error("Failed to get Azure user account info.", ex);
            throw new RuntimeException(ex);
        }
    }

    private List<Identity> getGroupIdentities(String azureAccessToken) {
        if (StringUtils.isEmpty(azureAccessToken)) {
            noAccessToken();
        }
        try {
            logger.debug("getGroupIdentities for logged in user");
            HttpResponse response = getFromAzure(azureAccessToken, getURL(AzureClientEndpoints.GROUP, ""));
            if(hasTokenExpired(response)) {
                //refresh token
                refreshAccessToken();
                //retry the request
                azureAccessToken = (String) ApiContext.getContext().getApiRequest().getAttribute(AzureConstants.AZURE_ACCESS_TOKEN);
                response = getFromAzure(azureAccessToken, getURL(AzureClientEndpoints.GROUP, ""));
            }
            
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode >= 300) {
                noAzure(response);
            }

            Map<String, Object> jsonData = CollectionUtils.toMap(jsonMapper.readValue(response.getEntity().getContent(), Map.class));
            List<AzureAccountInfo> searchResponseList = parseSearchResponseList(jsonData);

            List<Identity> groupIdentities = new ArrayList<Identity>();

            if(searchResponseList != null && !searchResponseList.isEmpty()){
                for(AzureAccountInfo userOrGroup : searchResponseList){
                    groupIdentities.add(userOrGroup.toIdentity(AzureConstants.GROUP_SCOPE));
                }
            }

            return groupIdentities;
        } catch (IOException e) {
            logger.error("Failed to get Azure group memberships.", e);
            throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR, AzureConstants.AZURE_CLIENT,
                    "Failed to get Azure group memberships.", null);
        } catch(ClientVisibleException ex) {
            logger.error("Failed to get Azure group memberships.", ex);
            throw ex;
        } catch(Exception ex) {
            logger.error("Failed to get Azure group memberships.", ex);
            throw new RuntimeException(ex);
        }
    }


    public String getAccessToken(String code) {
        if (!isConfigured()) {
            throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR, AzureConstants.CONFIG, "Azure Client and Tenant Id not configured", null);
        }
        
        logger.debug("getAccessToken from Azure");
    
        String[] split = code.split(":", 2);
        if (split.length != 2) {
            throw new ClientVisibleException(ResponseCodes.BAD_REQUEST, "MalformedCode");
        }
        String username = split[0];
        String domain = AzureConstants.AZURE_DOMAIN.get();
        //check if the username has a domain, if not then append the domain.
        if(domain != null && domain != "" && !username.endsWith("@"+domain)) {
            username = username + "@"+ domain;
        }
        
        //testuser1%40ZTAD.onmicrosoft.com&password=<PASSWORD>";

        String accessToken, refreshToken;
        try{   
            StringBuilder body = new StringBuilder("scope=openid&grant_type=password&resource=https%3A%2F%2Fgraph.windows.net");
            body.append("&client_id=");
            body.append(URLEncoder.encode(AzureConstants.AZURE_CLIENT_ID.get(), "UTF-8"));
            body.append("&username=");
            body.append(URLEncoder.encode(username, "UTF-8"));
            body.append("&password=");
            body.append(URLEncoder.encode(split[1], "UTF-8")); 
            
            HttpResponse response = Request.Post(AzureConstants.AUTHORITY)
                    .addHeader(AzureConstants.ACCEPT, AzureConstants.APPLICATION_FORM_URL_ENCODED)
                    .bodyString(body.toString(), ContentType.APPLICATION_FORM_URLENCODED).execute().returnResponse();
            int statusCode = response.getStatusLine().getStatusCode();
            if(statusCode >= 300) {
                noAzure(response);
            }

            Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent());
            accessToken = ObjectUtils.toString(jsonData.get("access_token"));
            refreshToken = ObjectUtils.toString(jsonData.get("refresh_token"));
            
            ApiContext.getContext().getApiRequest().setAttribute(AzureConstants.AZURE_ACCESS_TOKEN, accessToken);
            ApiContext.getContext().getApiRequest().setAttribute(AzureConstants.AZURE_REFRESH_TOKEN, refreshToken);

        } catch(ClientVisibleException ex) {
            throw ex;
        } catch(Exception ex) {
            throw new RuntimeException(ex);
        }

        return accessToken;

    }
    
    
    public void refreshAccessToken() {
        if (!isConfigured()) {
            throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR, AzureConstants.CONFIG, "Azure Client and Tenant Id not configured", null);
        }
        
        logger.debug("Azure accessToken expired, refreshing the token");
        
        String azureAccessToken = (String) ApiContext.getContext().getApiRequest().getAttribute(AzureConstants.AZURE_ACCESS_TOKEN);
        if (StringUtils.isEmpty(azureAccessToken)) {
            noAccessToken();
        }
        
        String azureRefreshToken = (String) ApiContext.getContext().getApiRequest().getAttribute(AzureConstants.AZURE_REFRESH_TOKEN);
        if (StringUtils.isEmpty(azureRefreshToken)) {
            noRefreshToken();
        }
        try{
            //post to the microsoft azure authority
            HttpResponse response = Request.Post(AzureConstants.AUTHORITY)
                    .addHeader(AzureConstants.ACCEPT, AzureConstants.APPLICATION_FORM_URL_ENCODED)
                    .bodyString("grant_type=refresh_token&refresh_token="+azureRefreshToken, ContentType.APPLICATION_FORM_URLENCODED)
                    .execute().returnResponse();
            int statusCode = response.getStatusLine().getStatusCode();
            if(statusCode >= 300) {
                noAzure(response);
            }
            
            Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent());
            
            String newAccessToken = ObjectUtils.toString(jsonData.get("access_token"));
            String newRefreshToken = ObjectUtils.toString(jsonData.get("refresh_token"));
            ApiContext.getContext().getApiRequest().setAttribute(AzureConstants.AZURE_ACCESS_TOKEN, newAccessToken);
            ApiContext.getContext().getApiRequest().setAttribute(AzureConstants.AZURE_REFRESH_TOKEN, newRefreshToken);
            logger.debug("Storing the new Azure access token to the Account");
            azureTokenUtil.refreshAccessToken();
    
        } catch(ClientVisibleException ex) {
            logger.error("Failed to refreshAccessToken for Azure user.", ex);
            throw ex;
        } catch(Exception ex) {
            logger.error("Failed to refreshAccessToken for Azure user.", ex);
            throw new RuntimeException(ex);
        }

    }

    public List<AzureAccountInfo> parseSearchResponseList(Map<String, Object> jsonData) {
        List<Object> azureValueList = (List<Object>)CollectionUtils.toList(jsonData.get("value"));
        List<AzureAccountInfo> azureUserOrGroupList = new ArrayList<AzureAccountInfo>();
        if (azureValueList != null && !azureValueList.isEmpty())
        {
            for(Object azureValue : azureValueList) {
                Map<String, Object> result = CollectionUtils.toMap(azureValue);
                String objectType = ObjectUtils.toString(result.get("objectType"));
                if(objectType != null && (objectType.equalsIgnoreCase("User") || objectType.equalsIgnoreCase("Group"))) {
                    AzureAccountInfo userOrGroupInfo = jsonToAzureAccountInfo(result);
                    azureUserOrGroupList.add(userOrGroupInfo);
                }
            }
        }
        return azureUserOrGroupList;
    }

    public AzureAccountInfo jsonToAzureAccountInfo(Map<String, Object> jsonData) {
        String objectId = ObjectUtils.toString(jsonData.get("objectId"));
        String objectType = ObjectUtils.toString(jsonData.get("objectType"));
        String userPrincipalName = ObjectUtils.toString(jsonData.get("userPrincipalName"));
        String accountName = ObjectUtils.toString(jsonData.get("mailNickname"));

        if("Group".equalsIgnoreCase(objectType)) {
            accountName = ObjectUtils.toString(jsonData.get("displayName"));
        }

        String name = ObjectUtils.toString(jsonData.get("displayName"));
        if (StringUtils.isBlank(name)) {
            name = accountName;
        }
        //String profilePicture = ObjectUtils.toString(jsonData.get(GithubConstants.PROFILE_PICTURE));
        return new AzureAccountInfo(objectId, accountName, null, userPrincipalName, name);
    }

    public boolean hasTokenExpired(HttpResponse response) throws IOException {
        int statusCode = response.getStatusLine().getStatusCode();
        if(statusCode == 401) {
            //if token expired, refresh token.
            Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent());
            Map<String, Object> azureError = CollectionUtils.toMap(jsonData.get("odata.error"));
            String azureCode = ObjectUtils.toString(azureError.get("code"));
            if("Authentication_ExpiredToken".equalsIgnoreCase(azureCode)) {
                return true;
            }
         }
        return false;
    }

    public HttpResponse getFromAzure(String azureAccessToken, String url) throws IOException {

        HttpResponse response = Request.Get(url).addHeader(AzureConstants.AUTHORIZATION, "Bearer " +
                "" + azureAccessToken).addHeader(AzureConstants.ACCEPT, AzureConstants.APPLICATION_JSON).execute().returnResponse();
        
        logger.debug("Response from Azure API: "+ response.getStatusLine());

        return response;
    }    

    private void noAccessToken() {
        throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR,
                "AzureAccessToken", "No Azure Access token", null);
    }
    
    private void noRefreshToken() {
        throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR,
                "AzureRefreshToken", "No Azure Refresh token", null);
    }
    
    public void noAzure(HttpResponse response) {
        int statusCode = response.getStatusLine().getStatusCode();

        try {
            Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent());
            String azureError = (String)jsonData.get("error");
            String azureErrorDesc = (String)jsonData.get("error_description");
            if("invalid_grant".equalsIgnoreCase(azureError) || "unauthorized_client".equalsIgnoreCase(azureError)) {
                throw new ClientVisibleException(ResponseCodes.UNAUTHORIZED);
            }
            logger.error("Error received from Azure: " + azureError);
            logger.error("Error Description received from Azure: " + azureErrorDesc);
            throw new ClientVisibleException(ResponseCodes.SERVICE_UNAVAILABLE, AzureConstants.AZURE_ERROR,
                    "Error from Azure: " + Integer.toString(statusCode), "Details: " +azureError + ", Description: "+azureErrorDesc);
        } catch(ClientVisibleException ex) {
            logger.error("Got error from Azure.", ex);
            throw ex;
        } catch(Exception ex) {
            throw new ClientVisibleException(ResponseCodes.SERVICE_UNAVAILABLE, AzureConstants.AZURE_ERROR,
                    "Error Response from Azure", "Status code from Azure: " + Integer.toString(statusCode));
        } 
    }

    public String getURL(AzureClientEndpoints val, String objectId) {
        String apiEndpoint = AzureConstants.GRAPH_API_ENDPOINT;
        String tenantId = AzureConstants.AZURE_TENANT_ID.get();

        String toReturn;
        switch (val) {
            case USERS:
                toReturn = apiEndpoint + tenantId + "/users/" + objectId;
                break;
{'fraction_non_alphanumeric': 0.10083212613280329, 'fraction_numerical': 0.0020213590270525216, 'mean_word_length': 2.6796826577414157, 'pattern_counts': {'":': 2, '<': 43, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3442490', 'n_tokens_mistral': 7591, 'n_tokens_neox': 7454, 'n_words': 1433}