Beispiel #1
0
// on success, ppOutTokenSuccessResponse will be non-null
// on error, ppOutTokenErrorResponse might be non-null (it will carry error info returned by the server if any)
// delete both when done, whether invocation is successful or not, using OidcTokenSuccessResponseDelete and OidcErrorResponseDelete
SSOERROR
OidcClientAcquireTokensBySolutionUserCredentials(
    PCOIDC_CLIENT p,
    PCSTRING pszCertificateSubjectDN,
    PCSTRING pszPrivateKeyPEM,
    PCSTRING pszScope,
    POIDC_TOKEN_SUCCESS_RESPONSE* ppOutTokenSuccessResponse, /* OUT */
    POIDC_ERROR_RESPONSE* ppOutTokenErrorResponse /* OUT */)
{
    SSOERROR e = SSOERROR_NONE;
    PSSO_KEY_VALUE_PAIR* ppPairs = NULL;
    int parameterCount = 3;
    PSTRING pszAssertionPayload = NULL;
    PSTRING pszAssertionJwtString = NULL;

    BAIL_ON_NULL_ARGUMENT(p);
    BAIL_ON_NULL_ARGUMENT(pszCertificateSubjectDN);
    BAIL_ON_NULL_ARGUMENT(pszPrivateKeyPEM);
    BAIL_ON_NULL_ARGUMENT(pszScope);
    BAIL_ON_NULL_ARGUMENT(ppOutTokenSuccessResponse);
    BAIL_ON_NULL_ARGUMENT(ppOutTokenErrorResponse);

    if (p->pszClientID != NULL)
    {
        parameterCount++;
    }

    e = OidcClientBuildSolutionUserAssertionPayload(p, pszCertificateSubjectDN, &pszAssertionPayload);
    BAIL_ON_ERROR(e);

    e = SSOJwtCreateSignedJwtString(pszAssertionPayload, pszPrivateKeyPEM, &pszAssertionJwtString);
    BAIL_ON_ERROR(e);

    e = SSOMemoryAllocateArray(parameterCount, sizeof(PSSO_KEY_VALUE_PAIR), (void**) &ppPairs);
    BAIL_ON_ERROR(e);
    e = SSOKeyValuePairNew(&ppPairs[0], "grant_type", "urn:vmware:grant_type:solution_user_credentials");
    BAIL_ON_ERROR(e);
    e = SSOKeyValuePairNew(&ppPairs[1], "solution_user_assertion", pszAssertionJwtString);
    BAIL_ON_ERROR(e);
    e = SSOKeyValuePairNew(&ppPairs[2], "scope", pszScope);
    BAIL_ON_ERROR(e);
    if (p->pszClientID != NULL)
    {
        e = SSOKeyValuePairNew(&ppPairs[3], "client_id", p->pszClientID);
        BAIL_ON_ERROR(e);
    }

    e = OidcClientAcquireTokens(p, ppPairs, parameterCount, ppOutTokenSuccessResponse, ppOutTokenErrorResponse);
    BAIL_ON_ERROR(e);

error:

    SSOMemoryFreeArrayOfObjects((void**) ppPairs, parameterCount, (GenericDestructorFunction) SSOKeyValuePairDelete);
    SSOStringFree(pszAssertionPayload);
    SSOStringFree(pszAssertionJwtString);

    return e;
}
Beispiel #2
0
void
SSOKeyValuePairDelete(
    PSSO_KEY_VALUE_PAIR p)
{
    if (p != NULL)
    {
        SSOStringFree(p->pszKey);
        SSOStringFree(p->pszValue);
        SSOMemoryFree(p, sizeof(SSO_KEY_VALUE_PAIR));
    }
}
Beispiel #3
0
void
OidcClientDelete(
    POIDC_CLIENT p)
{
    if (p != NULL)
    {
        SSOStringFree(p->pszServer);
        SSOStringFree(p->pszClientID);
        SSOStringFree(p->pszTlsCAPath);
        SSOStringFree(p->pszTokenEndpointUrl);
        SSOStringFree(p->pszSigningCertificatePEM);
        SSOCdcDelete(p->pClientDCCache);
        SSOMemoryFree(p, sizeof(OIDC_CLIENT));
    }
}
Beispiel #4
0
SSOERROR
SSOCdcGetAffinitizedHost(
    PCSSO_CDC pCdc,
    PCSTRING domainName,
    int cdcFlags,
    PSTRING* ppszAffinitizedHost)
{
    SSOERROR e = SSOERROR_NONE;

    DWORD dwError = 0;
    PVMAFD_SERVER pServer = NULL;
    PCDC_DC_INFO_A pDCInfo = NULL;
    PSTRING pszAffinitizedHost = NULL;

    if (pCdc == NULL || ppszAffinitizedHost == NULL)
    {
        e = SSOERROR_INVALID_ARGUMENT;
        BAIL_ON_ERROR(e);
    }

    dwError = (*(DWORD (*)(PCSTR, PCSTR, PCSTR, PVMAFD_SERVER*))pCdc->pVmAfdOpenServer)(NULL, NULL, NULL, &pServer);
    if (dwError != 0)
    {
        e = SSOERROR_VMAFD_CALL_FAILURE;
        BAIL_ON_ERROR(e);
    }

    dwError = (*(DWORD (*)(PVMAFD_SERVER, PCSTR, GUID_A, PCSTR, DWORD, PCDC_DC_INFO_A*))pCdc->pCdcGetDCName)(pServer, domainName, NULL, NULL, cdcFlags, &pDCInfo);
    if (dwError != 0)
    {
        e = SSOERROR_VMAFD_CALL_FAILURE;
        BAIL_ON_ERROR(e);
    }

    e = SSOStringAllocate(pDCInfo->pszDCName, &pszAffinitizedHost);
    BAIL_ON_ERROR(e);

    *ppszAffinitizedHost = pszAffinitizedHost;

    error:

    if (e != SSOERROR_NONE)
    {
        SSOStringFree(pszAffinitizedHost);
    }

    // cleanup
    if (pServer != NULL)
    {
        (*(VOID (*)(PVMAFD_SERVER))pCdc->pVmAfdCloseServer)(pServer);
    }

    if (pDCInfo != NULL)
    {
        (*(VOID (*)(PCDC_DC_INFO_A))pCdc->pCdcFreeDomainControllerInfo)(pDCInfo);
    }

    return e;
}
SSOERROR
IdmResourceServerDelete(
    PCREST_CLIENT pClient,
    PCSTRING tenant,
    PCSTRING name,
    REST_SERVER_ERROR** ppError)
{
    SSOERROR e = SSOERROR_NONE;
    PSTRING resourceUri = NULL;
    REST_SERVER_ERROR* pError = NULL;

    if (pClient == NULL || IS_NULL_OR_EMPTY_STRING(tenant) || IS_NULL_OR_EMPTY_STRING(name)
        || ppError == NULL)
    {
        e = SSOERROR_INVALID_ARGUMENT;
        BAIL_ON_ERROR(e);
    }

    e = RestBuildResourceUri(
        pClient,
        TENANT_URI,
        tenant,
        "resourceserver",
        name,
        NULL,
        &resourceUri);
    BAIL_ON_ERROR(e);

    e = RestBuildAndExecuteHttp(
        NULL,
        NULL,
        pClient->pAccessToken,
        resourceUri,
        REST_HTTP_METHOD_TYPE_DELETE,
        NULL,
        NULL,
        pClient->tlsCAPath,
        &pError);
    BAIL_ON_ERROR(e);

    error:

    if (e != SSOERROR_NONE)
    {
        *ppError = pError;
    }

    // cleanup
    SSOStringFree(resourceUri);

    return e;
}
SSOERROR
IdmResourceServerGetAll(
    PCREST_CLIENT pClient,
    PCSTRING tenant,
    IDM_RESOURCE_SERVER_ARRAY_DATA** ppResourceServerArrayReturn,
    REST_SERVER_ERROR** ppError)
{
    SSOERROR e = SSOERROR_NONE;
    PSTRING resourceUri = NULL;
    IDM_RESOURCE_SERVER_ARRAY_DATA* pResourceServerArrayReturn = NULL;
    REST_SERVER_ERROR* pError = NULL;

    if (pClient == NULL || IS_NULL_OR_EMPTY_STRING(tenant) || ppResourceServerArrayReturn == NULL
        || ppError == NULL)
    {
        e = SSOERROR_INVALID_ARGUMENT;
        BAIL_ON_ERROR(e);
    }

    e = RestBuildResourceUri(
        pClient,
        TENANT_POST_URI,
        tenant,
        "resourceserver",
        NULL,
        NULL,
        &resourceUri);
    BAIL_ON_ERROR(e);

    e = RestBuildAndExecuteHttp(
        NULL,
        NULL,
        pClient->pAccessToken,
        resourceUri,
        REST_HTTP_METHOD_TYPE_POST,
        (JsonToDataObjectFunc) IdmJsonToResourceServerArrayData,
        (void**) &pResourceServerArrayReturn,
        pClient->tlsCAPath,
        &pError);
    BAIL_ON_ERROR(e);

    *ppResourceServerArrayReturn = pResourceServerArrayReturn;

    // debug
    if (DEBUG)
    {
        RestDebugJsonArray(*ppResourceServerArrayReturn, (DataObjectToJsonFunc) IdmResourceServerArrayDataToJson);
    }

    error:

    if (e != SSOERROR_NONE)
    {
        IdmResourceServerArrayDataDelete(pResourceServerArrayReturn);
        *ppError = pError;
    }

    // cleanup
    SSOStringFree(resourceUri);

    return e;
}
Beispiel #7
0
static
SSOERROR
OidcClientAcquireTokens(
    PCOIDC_CLIENT p,
    PSSO_KEY_VALUE_PAIR* ppPairs,
    size_t parameterCount,
    POIDC_TOKEN_SUCCESS_RESPONSE* ppOutTokenSuccessResponse, /* OUT */
    POIDC_ERROR_RESPONSE*   ppOutTokenErrorResponse /* OUT */)
{
    SSOERROR e = SSOERROR_NONE;

    POIDC_TOKEN_SUCCESS_RESPONSE pOutTokenSuccessResponse = NULL;
    POIDC_ERROR_RESPONSE   pOutTokenErrorResponse = NULL;

    PSSO_HTTP_CLIENT pHttpClient = NULL;
    PSTRING pszJsonResponse = NULL;
    PSTRING pszAffinitizedTokenEndpointUrl = NULL;
    PSTRING pszAffinitizedHost = NULL;
    long httpStatusCode = 0;

    if (p->pClientDCCache != NULL) // highAvailabilityEnabled
    {
        e = SSOCdcGetAffinitizedHost(
            p->pClientDCCache,
            NULL, // PCSTRING domainName
            0, // int cdcFlags,
            &pszAffinitizedHost);
        BAIL_ON_ERROR(e);

        if (!SSOStringEqual(pszAffinitizedHost, p->pszServer))
        {
            e = SSOStringReplace(p->pszTokenEndpointUrl, p->pszServer, pszAffinitizedHost, &pszAffinitizedTokenEndpointUrl);
            BAIL_ON_ERROR(e);
        }
    }

    e = SSOHttpClientNew(&pHttpClient, p->pszTlsCAPath);
    BAIL_ON_ERROR(e);
    e = SSOHttpClientSendPostForm(
        pHttpClient,
        (pszAffinitizedTokenEndpointUrl != NULL) ? pszAffinitizedTokenEndpointUrl : p->pszTokenEndpointUrl,
        NULL, // pszHeaders
        0, // headerCount
        ppPairs,
        parameterCount,
        &pszJsonResponse,
        &httpStatusCode);
    BAIL_ON_ERROR(e);

    if (200 == httpStatusCode)
    {
        e = OidcTokenSuccessResponseParse(&pOutTokenSuccessResponse, pszJsonResponse);
        BAIL_ON_ERROR(e);
    }
    else
    {
        e = OidcErrorResponseParse(&pOutTokenErrorResponse, pszJsonResponse);
        BAIL_ON_ERROR(e);
    }

    *ppOutTokenSuccessResponse = pOutTokenSuccessResponse;
    *ppOutTokenErrorResponse = pOutTokenErrorResponse;
    if (pOutTokenErrorResponse != NULL)
    {
        // if server return error response, we translate that into an SSOERROR code
        e = OidcErrorResponseGetErrorCode(pOutTokenErrorResponse);
    }

error:
    SSOHttpClientDelete(pHttpClient);
    SSOStringFree(pszAffinitizedTokenEndpointUrl);
    SSOStringFree(pszAffinitizedHost);
    SSOStringFree(pszJsonResponse);
    return e;
}
Beispiel #8
0
static
SSOERROR
OidcClientBuildSolutionUserAssertionPayload(
    PCOIDC_CLIENT p,
    PCSTRING pszCertificateSubjectDN,
    PSTRING* ppsz /* OUT */)
{
    SSOERROR e = SSOERROR_NONE;
    PSTRING psz = NULL;
    PSSO_JSON pJson = NULL;
    PSSO_JSON pJsonValue = NULL; // do not delete this
    PSTRING pszJwtID = NULL;
    time_t currentTime = time(NULL);

    // use current time as the jwt-id (jti), not as good as uuid but simpler to generate
    e = SSOStringAllocateFromInt(currentTime, &pszJwtID);
    BAIL_ON_ERROR(e);

    e = SSOJsonObjectNew(&pJson);
    BAIL_ON_ERROR(e);

    e = SSOJsonStringNew(&pJsonValue, "solution_user_assertion");
    BAIL_ON_ERROR(e);
    e = SSOJsonObjectSet(pJson, "token_class", pJsonValue);
    BAIL_ON_ERROR(e);
    SSOJsonDelete(pJsonValue);
    pJsonValue = NULL;

    e = SSOJsonStringNew(&pJsonValue, "Bearer");
    BAIL_ON_ERROR(e);
    e = SSOJsonObjectSet(pJson, "token_type", pJsonValue);
    BAIL_ON_ERROR(e);
    SSOJsonDelete(pJsonValue);
    pJsonValue = NULL;

    e = SSOJsonStringNew(&pJsonValue, pszJwtID);
    BAIL_ON_ERROR(e);
    e = SSOJsonObjectSet(pJson, "jti", pJsonValue);
    BAIL_ON_ERROR(e);
    SSOJsonDelete(pJsonValue);
    pJsonValue = NULL;

    e = SSOJsonStringNew(&pJsonValue, pszCertificateSubjectDN);
    BAIL_ON_ERROR(e);
    e = SSOJsonObjectSet(pJson, "iss", pJsonValue);
    BAIL_ON_ERROR(e);
    SSOJsonDelete(pJsonValue);
    pJsonValue = NULL;

    e = SSOJsonStringNew(&pJsonValue, pszCertificateSubjectDN);
    BAIL_ON_ERROR(e);
    e = SSOJsonObjectSet(pJson, "sub", pJsonValue);
    BAIL_ON_ERROR(e);
    SSOJsonDelete(pJsonValue);
    pJsonValue = NULL;

    e = SSOJsonStringNew(&pJsonValue, p->pszTokenEndpointUrl);
    BAIL_ON_ERROR(e);
    e = SSOJsonObjectSet(pJson, "aud", pJsonValue);
    BAIL_ON_ERROR(e);
    SSOJsonDelete(pJsonValue);
    pJsonValue = NULL;

    e = SSOJsonLongNew(&pJsonValue, currentTime);
    BAIL_ON_ERROR(e);
    e = SSOJsonObjectSet(pJson, "iat", pJsonValue);
    BAIL_ON_ERROR(e);
    SSOJsonDelete(pJsonValue);
    pJsonValue = NULL;

    e = SSOJsonToString(pJson, &psz);
    BAIL_ON_ERROR(e);

    *ppsz = psz;

error:

    if (e != SSOERROR_NONE)
    {
        SSOStringFree(psz);
    }

    SSOJsonDelete(pJson); // pJsonValue's will be deleted by the this call
    SSOJsonDelete(pJsonValue);
    SSOStringFree(pszJwtID);

    return e;
}
Beispiel #9
0
SSOERROR
SSOStringReplace(
    PCSTRING pszInput,
    PCSTRING pszFind,
    PCSTRING pszReplace,
    PSTRING* ppsz /* OUT */)
{
    SSOERROR e = SSOERROR_NONE;
    PSTRING psz = NULL;
    PSTRING pszPrefix = NULL;
    PSTRING pszSuffix = NULL;
    PSTRING pszPrefixPlusReplace = NULL;
    char* pszLocation = strstr(pszInput, pszFind);

    ASSERT_NOT_NULL(pszInput);
    ASSERT_NOT_NULL(pszFind);
    ASSERT_NOT_NULL(pszReplace);
    ASSERT_NOT_NULL(ppsz);

    if (pszLocation != NULL)
    {
        size_t inputLength = strlen(pszInput);
        size_t findLength = strlen(pszFind);
        size_t locationLength = strlen(pszLocation);
        size_t prefixLength = inputLength - locationLength;
        size_t suffixLength = locationLength - findLength;

        if (prefixLength > 0)
        {
            e = SSOStringAllocateSubstring(pszInput, 0, prefixLength - 1, &pszPrefix);
            BAIL_ON_ERROR(e);
        }
        else
        {
            e = SSOStringAllocate("", &pszPrefix);
            BAIL_ON_ERROR(e);
        }

        if (suffixLength > 0)
        {
            e = SSOStringAllocateSubstring(pszInput, inputLength - suffixLength, inputLength - 1, &pszSuffix);
            BAIL_ON_ERROR(e);
        }
        else
        {
            e = SSOStringAllocate("", &pszSuffix);
            BAIL_ON_ERROR(e);
        }

        e = SSOStringConcatenate(pszPrefix, pszReplace, &pszPrefixPlusReplace);
        BAIL_ON_ERROR(e);

        e = SSOStringConcatenate(pszPrefixPlusReplace, pszSuffix, &psz);
        BAIL_ON_ERROR(e);
    }
    else
    {
        // not found, return a copy of the input
        e = SSOStringAllocate(pszInput, &psz);
        BAIL_ON_ERROR(e);
    }

    *ppsz = psz;

error:

    if (e != SSOERROR_NONE)
    {
        SSOStringFree(psz);
    }

    SSOStringFree(pszPrefix);
    SSOStringFree(pszSuffix);
    SSOStringFree(pszPrefixPlusReplace);

    return e;
}
Beispiel #10
0
SSOERROR
IdmTenantCreate(
    PCREST_CLIENT pClient,
    const IDM_TENANT_DATA* pTenant,
    IDM_TENANT_DATA** ppTenantReturn,
    REST_SERVER_ERROR** ppError)
{
    SSOERROR e = SSOERROR_NONE;
    PSTRING resourceUri = NULL;
    IDM_TENANT_DATA* pTenantReturn = NULL;
    REST_SERVER_ERROR* pError = NULL;

    if (pClient == NULL || pTenant == NULL || ppTenantReturn == NULL || ppError == NULL)
    {
        e = SSOERROR_INVALID_ARGUMENT;
        BAIL_ON_ERROR(e);
    }

    e = RestBuildResourceUri(
        pClient,
        TENANT_URI,
        NULL,
        NULL,
        NULL,
        NULL,
        &resourceUri);
    BAIL_ON_ERROR(e);

    e = RestBuildAndExecuteHttp(
        pTenant,
        (DataObjectToJsonFunc) IdmTenantDataToJson,
        pClient->pAccessToken,
        resourceUri,
        REST_HTTP_METHOD_TYPE_POST,
        (JsonToDataObjectFunc) IdmJsonToTenantData,
        (void**) &pTenantReturn,
        pClient->tlsCAPath,
        &pError);
    BAIL_ON_ERROR(e);

    *ppTenantReturn = pTenantReturn;

    // debug
    if (DEBUG)
    {
        RestDebugJsonObject(*ppTenantReturn, (DataObjectToJsonFunc) IdmTenantDataToJson);
    }

    error:

    if (e != SSOERROR_NONE)
    {
        IdmTenantDataDelete(pTenantReturn);
        *ppError = pError;
    }

    // cleanup
    SSOStringFree(resourceUri);

    return e;
}
Beispiel #11
0
SSOERROR
IdmTenantSearch(
    PCREST_CLIENT pClient,
    PCSTRING tenant,
    PCSTRING domain,
    PCSTRING query,
    IDM_MEMBER_TYPE memberType,
    IDM_SEARCH_TYPE searchBy,
    size_t limit,
    IDM_SEARCH_RESULT_DATA** ppSearchResultReturn,
    REST_SERVER_ERROR** ppError)
{
    SSOERROR e = SSOERROR_NONE;
    PSTRING s = NULL;
    PSTRING resourceUri = NULL;
    IDM_SEARCH_RESULT_DATA* pSearchResultReturn = NULL;
    REST_SERVER_ERROR* pError = NULL;

    if (pClient == NULL || ppSearchResultReturn == NULL || ppError == NULL)
    {
        e = SSOERROR_INVALID_ARGUMENT;
        BAIL_ON_ERROR(e);
    }

    e = RestBuildResourceUri(
        pClient,
        TENANT_POST_URI,
        tenant,
        "search",
        NULL,
        NULL,
        &resourceUri);
    BAIL_ON_ERROR(e);

    e = RestAppendQueryStringOnResourceUri("domain", domain, true, resourceUri, &resourceUri);
    BAIL_ON_ERROR(e);

    e = RestAppendQueryStringOnResourceUri("query", query, false, resourceUri, &resourceUri);
    BAIL_ON_ERROR(e);

    e = RestAppendQueryStringOnResourceUri("type", IDM_MEMBER_TYPE_ENUMS[memberType], false, resourceUri, &resourceUri);
    BAIL_ON_ERROR(e);

    e = RestAppendQueryStringOnResourceUri("searchBy", IDM_SEARCH_TYPE_ENUMS[searchBy], false, resourceUri, &resourceUri);
    BAIL_ON_ERROR(e);

    e = SSOStringAllocateFromInt((int) limit, &s);
    BAIL_ON_ERROR(e);

    e = RestAppendQueryStringOnResourceUri("limit", s, false, resourceUri, &resourceUri);
    BAIL_ON_ERROR(e);

    e = RestBuildAndExecuteHttp(
        NULL,
        NULL,
        pClient->pAccessToken,
        resourceUri,
        REST_HTTP_METHOD_TYPE_POST,
        (JsonToDataObjectFunc) IdmJsonToSearchResultData,
        (void**) &pSearchResultReturn,
        pClient->tlsCAPath,
        &pError);
    BAIL_ON_ERROR(e);

    *ppSearchResultReturn = pSearchResultReturn;

    // debug
    if (DEBUG)
    {
        RestDebugJsonObject(*ppSearchResultReturn, (DataObjectToJsonFunc) IdmSearchResultDataToJson);
    }

    error:

    if (e != SSOERROR_NONE)
    {
        IdmSearchResultDataDelete(pSearchResultReturn);
        *ppError = pError;
    }

    // cleanup
    SSOStringFree(s);
    SSOStringFree(resourceUri);

    return e;
}
Beispiel #12
0
SSOERROR
IdmTenantGetConfig(
    PCREST_CLIENT pClient,
    PCSTRING tenant,
    IDM_TENANT_CONFIG_TYPE_ARRAY* pTenantConfigTypes,
    IDM_TENANT_CONFIGURATION_DATA** ppTenantConfigurationReturn,
    REST_SERVER_ERROR** ppError)
{
    SSOERROR e = SSOERROR_NONE;
    PSTRING resourceUri = NULL;
    IDM_TENANT_CONFIGURATION_DATA* pTenantConfigurationReturn = NULL;
    REST_SERVER_ERROR* pError = NULL;
    size_t i = 0;

    if (pClient == NULL || IS_NULL_OR_EMPTY_STRING(tenant) || ppTenantConfigurationReturn == NULL
        || ppError == NULL)
    {
        e = SSOERROR_INVALID_ARGUMENT;
        BAIL_ON_ERROR(e);
    }

    e = RestBuildResourceUri(
        pClient,
        TENANT_POST_URI,
        tenant,
        "config",
        NULL,
        NULL,
        &resourceUri);
    BAIL_ON_ERROR(e);

    for (i = 0; i < pTenantConfigTypes->length; i++)
    {
        e = RestAppendQueryStringOnResourceUri(
            "type",
            IDM_TENANT_CONFIG_TYPE_ENUMS[*(pTenantConfigTypes->ppEntry[i])],
            i == 0,
            resourceUri,
            &resourceUri);
        BAIL_ON_ERROR(e);
    }

    e = RestBuildAndExecuteHttp(
        NULL,
        NULL,
        pClient->pAccessToken,
        resourceUri,
        REST_HTTP_METHOD_TYPE_POST,
        (JsonToDataObjectFunc) IdmJsonToTenantConfigurationData,
        (void**) &pTenantConfigurationReturn,
        pClient->tlsCAPath,
        &pError);
    BAIL_ON_ERROR(e);

    *ppTenantConfigurationReturn = pTenantConfigurationReturn;

    // debug
    if (DEBUG)
    {
        RestDebugJsonObject(*ppTenantConfigurationReturn, (DataObjectToJsonFunc) IdmTenantConfigurationDataToJson);
    }

    error:

    if (e != SSOERROR_NONE)
    {
        IdmTenantConfigurationDataDelete(pTenantConfigurationReturn);
        *ppError = pError;
    }

    // cleanup
    SSOStringFree(resourceUri);

    return e;
}