diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/token/AIAccessTokenManager.java b/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/token/AIAccessTokenManager.java deleted file mode 100644 index d4c06c6c8c28..000000000000 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/token/AIAccessTokenManager.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. 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.wso2.carbon.ai.service.mgt.token; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonSyntaxException; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.concurrent.FutureCallback; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.apache.http.impl.nio.client.HttpAsyncClients; -import org.apache.http.message.BasicHeader; -import org.apache.http.protocol.HTTP; -import org.apache.http.util.EntityUtils; -import org.wso2.carbon.ai.service.mgt.exceptions.AIServerException; -import org.wso2.carbon.identity.core.util.IdentityUtil; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import static org.apache.axis2.transport.http.HTTPConstants.HEADER_CONTENT_TYPE; -import static org.apache.http.HttpHeaders.AUTHORIZATION; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.ACCESS_TOKEN_KEY; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.AI_SERVICE_KEY_PROPERTY_NAME; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.AI_TOKEN_ENDPOINT_PROPERTY_NAME; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.AI_TOKEN_SERVICE_MAX_RETRIES_PROPERTY_NAME; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.AI_TOKEN_SERVICE_TIMEOUT_PROPERTY_NAME; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.CONTENT_TYPE_FORM_URLENCODED; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.ErrorMessages.MAXIMUM_RETRIES_EXCEEDED; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.HTTP_BASIC; -import static org.wso2.carbon.identity.core.util.IdentityTenantUtil.getTenantDomainFromContext; - -/** - * The purpose of this class is to retrieve an active token to access the AI service. - */ -public class AIAccessTokenManager { - - private static volatile AIAccessTokenManager instance; // Volatile for thread safety. - private static final Object lock = new Object(); // Lock for synchronization. - - private static final Log LOG = LogFactory.getLog(AIAccessTokenManager.class); - - private static final String AI_KEY = IdentityUtil.getProperty(AI_SERVICE_KEY_PROPERTY_NAME); - private static final String AI_TOKEN_ENDPOINT = IdentityUtil.getProperty(AI_TOKEN_ENDPOINT_PROPERTY_NAME); - - private AccessTokenRequestHelper accessTokenRequestHelper; - - private String accessToken; - private final String clientId; - - private AIAccessTokenManager() { - - byte[] decodedBytes = Base64.getDecoder().decode(AI_KEY); - String decodedString = new String(decodedBytes, StandardCharsets.UTF_8); - String[] parts = decodedString.split(":"); - if (parts.length == 2) { - this.clientId = parts[0]; // Extract clientId. - } else { - throw new IllegalArgumentException("Invalid AI service key."); - } - } - - /** - * Get the singleton instance of the AIAccessTokenManager. - * - * @return The singleton instance. - */ - public static AIAccessTokenManager getInstance() { - - if (instance == null) { - synchronized (lock) { - if (instance == null) { - instance = new AIAccessTokenManager(); - } - } - } - return instance; - } - - /** - * Set the access token request helper. - * - * @param helper The access token request helper. - */ - protected void setAccessTokenRequestHelper(AccessTokenRequestHelper helper) { - - this.accessTokenRequestHelper = helper; - } - - /** - * Get the access token. - * - * @param renewAccessToken Whether to renew the access token. - * @return The access token. - * @throws AIServerException If an error occurs while obtaining the access token. - */ - public String getAccessToken(boolean renewAccessToken) throws AIServerException { - - if (StringUtils.isEmpty(accessToken) || renewAccessToken) { - synchronized (AIAccessTokenManager.class) { - if (StringUtils.isEmpty(accessToken) || renewAccessToken) { - this.accessToken = accessTokenRequestHelper != null ? - accessTokenRequestHelper.requestAccessToken() : createDefaultHelper().requestAccessToken(); - } - } - } - return this.accessToken; - } - - private AccessTokenRequestHelper createDefaultHelper() { - - return new AccessTokenRequestHelper(AI_KEY, AI_TOKEN_ENDPOINT, - // Here we keep the default HTTP client to send the token request. - // We open and close it for each request. - HttpAsyncClients.createDefault()); - } - - /** - * Get the client ID. - * - * @return The client ID. - */ - public String getClientId() { - - return this.clientId; - } - - /** - * Helper class to request access token from the AI services. - */ - protected static class AccessTokenRequestHelper { - - private final CloseableHttpAsyncClient client; - private final Gson gson; - private final String key; - private final String aiServiceTokenEndpoint; - private static final int MAX_RETRIES = IdentityUtil.getProperty( - AI_TOKEN_SERVICE_MAX_RETRIES_PROPERTY_NAME) != null ? - Integer.parseInt(IdentityUtil.getProperty(AI_TOKEN_SERVICE_MAX_RETRIES_PROPERTY_NAME)) : 3; - private static final long TIMEOUT = IdentityUtil.getProperty(AI_TOKEN_SERVICE_TIMEOUT_PROPERTY_NAME) != null ? - Long.parseLong(IdentityUtil.getProperty(AI_TOKEN_SERVICE_TIMEOUT_PROPERTY_NAME)) : 3000; - - AccessTokenRequestHelper(String key, String tokenEndpoint, CloseableHttpAsyncClient client) { - - this.client = client; - this.gson = new GsonBuilder().create(); - this.key = key; - this.aiServiceTokenEndpoint = tokenEndpoint; - } - - /** - * Request access token to access the AI services. - * - * @return the JWT access token. - * @throws AIServerException If an error occurs while requesting the access token. - */ - public String requestAccessToken() throws AIServerException { - - String tenantDomain = getTenantDomainFromContext(); - LOG.info("Initiating access token request for AI services from tenant: " + tenantDomain); - try { - client.start(); - for (int attempt = 0; attempt < MAX_RETRIES; attempt++) { - HttpPost post = new HttpPost(aiServiceTokenEndpoint); - post.setHeader(AUTHORIZATION, HTTP_BASIC + " " + key); - post.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_FORM_URLENCODED); - - StringEntity entity = new StringEntity("grant_type=client_credentials&tokenBindingId=" + - UUID.randomUUID()); - entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, CONTENT_TYPE_FORM_URLENCODED)); - post.setEntity(entity); - - CountDownLatch latch = new CountDownLatch(1); - final String[] accessToken = new String[1]; - client.execute(post, new FutureCallback() { - @Override - public void completed(HttpResponse response) { - - try { - if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { - String responseBody = EntityUtils.toString(response.getEntity()); - Map responseMap = gson.fromJson(responseBody, Map.class); - accessToken[0] = (String) responseMap.get(ACCESS_TOKEN_KEY); - } else { - LOG.error("Token request failed with status code: " + - response.getStatusLine().getStatusCode()); - } - } catch (IOException | JsonSyntaxException e) { - LOG.warn("Error parsing token response: " + e.getMessage(), e); - } finally { - latch.countDown(); - } - } - - @Override - public void failed(Exception e) { - - LOG.warn("Token request failed: " + e.getMessage(), e); - latch.countDown(); - } - - @Override - public void cancelled() { - - LOG.warn("Token request was cancelled"); - latch.countDown(); - } - }); - - if (latch.await(TIMEOUT, TimeUnit.MILLISECONDS)) { - if (accessToken[0] != null) { - return accessToken[0]; - } - } else { - LOG.warn("Token request timed out"); - } - // Wait before retrying. - TimeUnit.MILLISECONDS.sleep(500); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new AIServerException("Token request interrupted: " + e.getMessage(), e); - } catch (UnsupportedEncodingException e) { - throw new AIServerException("Error creating token request: " + e.getMessage(), e); - } finally { - try { - client.close(); - } catch (IOException e) { - LOG.error("Failed to close HTTP client: " + e.getMessage(), e); - } - } - // If it reaches this point. - throw new AIServerException("Failed to obtain access token after " + MAX_RETRIES + - " attempts.", MAXIMUM_RETRIES_EXCEEDED.getCode()); - } - } -} diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/util/AIHttpClientUtil.java b/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/util/AIHttpClientUtil.java deleted file mode 100644 index 297a0837085a..000000000000 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/util/AIHttpClientUtil.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. 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.wso2.carbon.ai.service.mgt.util; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.concurrent.FutureCallback; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.apache.http.impl.nio.client.HttpAsyncClients; -import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager; -import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor; -import org.apache.http.impl.nio.reactor.IOReactorConfig; -import org.apache.http.nio.reactor.ConnectingIOReactor; -import org.apache.http.util.EntityUtils; -import org.wso2.carbon.ai.service.mgt.exceptions.AIClientException; -import org.wso2.carbon.ai.service.mgt.exceptions.AIServerException; -import org.wso2.carbon.ai.service.mgt.token.AIAccessTokenManager; -import org.wso2.carbon.context.PrivilegedCarbonContext; -import org.wso2.carbon.identity.core.util.IdentityUtil; - -import java.io.IOException; -import java.io.Serializable; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - -import static org.apache.axis2.transport.http.HTTPConstants.HEADER_CONTENT_TYPE; -import static org.apache.http.HttpHeaders.AUTHORIZATION; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.CONTENT_TYPE_JSON; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.ErrorMessages.CLIENT_ERROR_WHILE_CONNECTING_TO_AI_SERVICE; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.ErrorMessages.ERROR_RETRIEVING_ACCESS_TOKEN; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.ErrorMessages.SERVER_ERROR_WHILE_CONNECTING_TO_AI_SERVICE; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.ErrorMessages.UNABLE_TO_ACCESS_AI_SERVICE_WITH_RENEW_ACCESS_TOKEN; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.HTTP_BEARER; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.HTTP_CONNECTION_POOL_SIZE_PROPERTY_NAME; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.HTTP_CONNECTION_TIMEOUT_PROPERTY_NAME; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.TENANT_CONTEXT_PREFIX; - -/** - * Utility class for AI Services to send HTTP requests. - */ -public class AIHttpClientUtil { - - private static final Log LOG = LogFactory.getLog(AIHttpClientUtil.class); - private static final ObjectMapper objectMapper = new ObjectMapper(); - - private static final int HTTP_CONNECTION_POOL_SIZE = IdentityUtil.getProperty( - HTTP_CONNECTION_POOL_SIZE_PROPERTY_NAME) != null ? Integer.parseInt(IdentityUtil.getProperty( - HTTP_CONNECTION_POOL_SIZE_PROPERTY_NAME)) : 20; - private static final int HTTP_CONNECTION_TIMEOUT = IdentityUtil.getProperty( - HTTP_CONNECTION_TIMEOUT_PROPERTY_NAME) != null ? Integer.parseInt(IdentityUtil.getProperty( - HTTP_CONNECTION_TIMEOUT_PROPERTY_NAME)) : 60000; // Making the default timeout 60 seconds. - - - // Singleton instance of CloseableHttpAsyncClient with connection pooling. - private static final CloseableHttpAsyncClient httpClient; - - static { - // Configure the IO reactor. - IOReactorConfig ioReactorConfig = IOReactorConfig.custom() - .setIoThreadCount(Runtime.getRuntime().availableProcessors()) - .setConnectTimeout(HTTP_CONNECTION_TIMEOUT) - .build(); - ConnectingIOReactor ioReactor; - try { - // Create the IO reactor. - ioReactor = new DefaultConnectingIOReactor(ioReactorConfig); - } catch (IOException e) { - throw new RuntimeException("Error initializing IO Reactor", e); - } - // Create a connection manager with the IO reactor. - PoolingNHttpClientConnectionManager connectionManager = new PoolingNHttpClientConnectionManager(ioReactor); - // Maximum total connections. - connectionManager.setMaxTotal(HTTP_CONNECTION_POOL_SIZE); - // Initialize the HttpClient with the connection manager. - httpClient = HttpAsyncClients.custom() - .setConnectionManager(connectionManager) - .build(); - // Start the HttpClient. - httpClient.start(); - // Add a shutdown hook to close the client when the application stops. - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - httpClient.close(); - } catch (IOException e) { - LOG.error("Error while shutting down HTTP client: " + e.getMessage()); - } - })); - } - - /** - * Execute a request to the AI service. - * - * @param path The endpoint to which the request should be sent. - * @param requestType The type of the request (GET, POST). - * @param requestBody The request body(Only for POST requests). - * @param aiServiceEndpoint The endpoint of the AI service. - * @return The response from the AI service as a map. - * @throws AIServerException If a server error occurred while accessing the AI service. - * @throws AIClientException If a client error occurred while accessing the AI service. - */ - public static Map executeRequest(String aiServiceEndpoint, String path, - Class requestType, Object requestBody) - throws AIServerException, AIClientException { - - String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); - - try { - String accessToken = AIAccessTokenManager.getInstance().getAccessToken(false); - String orgName = AIAccessTokenManager.getInstance().getClientId(); - - HttpUriRequest request = createRequest(aiServiceEndpoint + TENANT_CONTEXT_PREFIX + orgName + path, - requestType, accessToken, requestBody); - HttpResponseWrapper aiServiceResponse = executeRequestWithRetry(request); - - int statusCode = aiServiceResponse.getStatusCode(); - String responseBody = aiServiceResponse.getResponseBody(); - - if (statusCode >= 400) { - handleErrorResponse(statusCode, responseBody, tenantDomain); - } - return convertJsonStringToMap(responseBody); - } catch (IOException | ExecutionException e) { - throw new AIServerException("An error occurred while connecting to the AI Service.", - SERVER_ERROR_WHILE_CONNECTING_TO_AI_SERVICE.getCode(), e); - } catch (InterruptedException e) { - // Restore the interrupted status of the thread to ensure it is not swallowed - // and can be handled appropriately by other parts of the program. This is - // important for proper thread coordination and graceful shutdown in a - // multithreaded environment. - Thread.currentThread().interrupt(); - - // Wrap and rethrow the exception as a custom AIServerException to provide - // a meaningful error message and maintain the original exception for debugging. - throw new AIServerException("An error occurred while connecting to the AI Service.", - SERVER_ERROR_WHILE_CONNECTING_TO_AI_SERVICE.getCode(), e); - } - } - - private static HttpUriRequest createRequest(String url, Class requestType, - String accessToken, Object requestBody) throws IOException { - - HttpUriRequest request; - if (requestType == HttpPost.class) { - HttpPost post = new HttpPost(url); - if (requestBody != null) { - post.setEntity(new StringEntity(objectMapper.writeValueAsString(requestBody))); - } - request = post; - } else if (requestType == HttpGet.class) { - request = new HttpGet(url); - } else { - throw new IllegalArgumentException("Unsupported request type: " + requestType.getName()); - } - - request.setHeader(AUTHORIZATION, HTTP_BEARER + " " + accessToken); - request.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON); - return request; - } - - private static HttpResponseWrapper executeRequestWithRetry(HttpUriRequest request) - throws InterruptedException, ExecutionException, IOException, AIServerException { - - HttpResponseWrapper response = executeHttpRequest(request); - - if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { - String newAccessToken = AIAccessTokenManager.getInstance().getAccessToken(true); - if (newAccessToken == null) { - throw new AIServerException("Failed to renew access token.", ERROR_RETRIEVING_ACCESS_TOKEN.getCode()); - } - request.setHeader(AUTHORIZATION, HTTP_BEARER + " " + newAccessToken); - response = executeHttpRequest(request); - } - return response; - } - - private static void handleErrorResponse(int statusCode, String responseBody, String tenantDomain) - throws AIServerException, AIClientException { - - if (statusCode == HttpStatus.SC_UNAUTHORIZED) { - throw new AIServerException("Failed to access AI service with renewed access token for " + - "the tenant domain: " + tenantDomain, - UNABLE_TO_ACCESS_AI_SERVICE_WITH_RENEW_ACCESS_TOKEN.getCode()); - } else if (statusCode >= 400 && statusCode < 500) { - throw new AIClientException(new HttpResponseWrapper(statusCode, responseBody), - "Client error occurred from tenant: " + tenantDomain + " with status code: '" + statusCode - + "' while accessing AI service.", CLIENT_ERROR_WHILE_CONNECTING_TO_AI_SERVICE.getCode()); - } else if (statusCode >= 500) { - throw new AIServerException(new HttpResponseWrapper(statusCode, responseBody), - "Server error occurred from tenant: " + tenantDomain + " with status code: '" + statusCode - + "' while accessing AI service.", SERVER_ERROR_WHILE_CONNECTING_TO_AI_SERVICE.getCode()); - } - } - - private static Map convertJsonStringToMap(String jsonString) throws AIServerException { - - try { - return objectMapper.readValue(jsonString, Map.class); - } catch (IOException e) { - throw new AIServerException("Error occurred while parsing the JSON response from the AI service.", e); - } - } - - protected static HttpResponseWrapper executeHttpRequest(HttpUriRequest httpRequest) - throws InterruptedException, ExecutionException, IOException, AIServerException { - - Future apiResponse = AIHttpClientUtil.httpClient.execute(httpRequest, - new FutureCallback() { - @Override - public void completed(HttpResponse response) { - - LOG.info("API request completed with status code: " + response.getStatusLine().getStatusCode()); - } - - @Override - public void failed(Exception e) { - - LOG.error("API request failed: " + e.getMessage(), e); - } - - @Override - public void cancelled() { - - LOG.warn("API request was cancelled"); - } - }); - if (apiResponse == null) { - throw new AIServerException("Unable to get the response from the AI service.", - SERVER_ERROR_WHILE_CONNECTING_TO_AI_SERVICE.getCode()); - } - HttpResponse httpResponse = apiResponse.get(); - int status = httpResponse.getStatusLine().getStatusCode(); - String response = EntityUtils.toString(httpResponse.getEntity()); - return new HttpResponseWrapper(status, response); - } - - /** - * Wrapper class for HTTP response. - */ - public static class HttpResponseWrapper implements Serializable { - - private final int statusCode; - private final String responseBody; - - public HttpResponseWrapper(int statusCode, String responseBody) { - - this.statusCode = statusCode; - this.responseBody = responseBody; - } - - public int getStatusCode() { - - return statusCode; - } - - public String getResponseBody() { - - return responseBody; - } - } -} diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/token/AIAccessTokenManagerTest.java b/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/token/AIAccessTokenManagerTest.java deleted file mode 100644 index 1dfbb96d68f5..000000000000 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/token/AIAccessTokenManagerTest.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. 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.wso2.carbon.ai.service.mgt.token; - -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.concurrent.FutureCallback; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; -import org.wso2.carbon.ai.service.mgt.exceptions.AIServerException; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.Base64; -import java.util.concurrent.Future; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * Test class for AIAccessTokenManager. - */ -public class AIAccessTokenManagerTest { - - @Mock - private CloseableHttpAsyncClient mockHttpClient; - - @Mock - private Future mockFuture; - - @Mock - private HttpResponse mockResponse; - - @Mock - private StatusLine mockStatusLine; - - private AIAccessTokenManager tokenManager; - private TestAccessTokenRequestHelper testHelper; - private AIAccessTokenManager.AccessTokenRequestHelper helper; - - @BeforeMethod - public void setUp() throws NoSuchFieldException, IllegalAccessException { - - MockitoAnnotations.openMocks(this); - testHelper = new TestAccessTokenRequestHelper(mockHttpClient); - String key = Base64.getEncoder().encodeToString("testClientId:testClientSecret".getBytes()); - assignAIKey(key); - tokenManager = AIAccessTokenManager.getInstance(); - tokenManager.setAccessTokenRequestHelper(testHelper); - - helper = new AIAccessTokenManager.AccessTokenRequestHelper(key, "endpoint", mockHttpClient); - } - - @AfterMethod - public void tearDown() { - - // Reset other mocks and state. - tokenManager = null; - testHelper = null; - } - - @Test - public void testGetInstance() { - - AIAccessTokenManager instance1 = AIAccessTokenManager.getInstance(); - AIAccessTokenManager instance2 = AIAccessTokenManager.getInstance(); - Assert.assertSame(instance1, instance2, "getInstance should always return the same instance"); - } - - @Test - public void testGetAccessToken_Success() throws Exception { - - String expectedToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJ0ZXN0Q2xpZW50SWQifQ.signature"; - setupMockHttpResponse(HttpStatus.SC_OK, "{\"access_token\":\"" + expectedToken + "\"}"); - - String token = tokenManager.getAccessToken(true); - Assert.assertEquals(token, expectedToken); - Assert.assertEquals(tokenManager.getClientId(), "testClientId"); - } - - @Test - public void testGetAccessToken_Renewal() throws Exception { - - setupMockHttpResponse(HttpStatus.SC_OK, "{\"access_token\":\"oldToken\"}"); - String token1 = tokenManager.getAccessToken(false); - - setupMockHttpResponse(HttpStatus.SC_OK, "{\"access_token\":\"newToken\"}"); - String token2 = tokenManager.getAccessToken(true); - - Assert.assertNotEquals(token1, token2, "Tokens should be different after renewal"); - } - - @Test(expectedExceptions = AIServerException.class) - public void testGetAccessToken_HttpError() throws Exception { - - setupMockHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Internal Server Error"); - tokenManager.getAccessToken(false); - } - - @Test(expectedExceptions = AIServerException.class) - public void testGetAccessToken_Timeout() throws Exception { - - when(mockHttpClient.execute(any(HttpPost.class), any(FutureCallback.class))).thenReturn(mockFuture); - when(mockFuture.get()).thenThrow(new InterruptedException("Timeout")); - tokenManager.getAccessToken(true); - } - - @Test(expectedExceptions = AIServerException.class) - public void testGetAccessToken_MaxRetriesExceeded() throws Exception { - setupMockHttpResponse(HttpStatus.SC_BAD_REQUEST, "Bad Request"); - tokenManager.getAccessToken(false); - } - - @Test(expectedExceptions = AIServerException.class) - public void testGetAccessToken_InterruptedDuringRequest() throws Exception { - when(mockHttpClient.execute(any(HttpPost.class), any(FutureCallback.class))).thenAnswer(invocation -> { - Thread.currentThread().interrupt(); - throw new InterruptedException("Request interrupted"); - }); - - try { - tokenManager.getAccessToken(false); - } finally { - Assert.assertTrue(Thread.interrupted(), "Thread interrupt flag should be cleared"); - } - } - - @Test - public void completed_InvalidJsonResponse() throws Exception { - setupMockHttpResponse(HttpStatus.SC_OK, "Invalid JSON"); - - try { - tokenManager.getAccessToken(true); - Assert.fail("Expected AIServerException to be thrown"); - } catch (AIServerException e) { - Assert.assertTrue(e.getMessage().contains("Failed to obtain access token after 3 attempts.")); - } - } - - @Test - public void testFailedScenario() throws Exception { - ArgumentCaptor> captor = ArgumentCaptor.forClass(FutureCallback.class); - doNothing().when(mockHttpClient).start(); - doAnswer(invocation -> { - FutureCallback callback = captor.getValue(); - callback.failed(new Exception("Test Exception")); - return null; - }).when(mockHttpClient).execute(any(), captor.capture()); - - try { - helper.requestAccessToken(); - } catch (AIServerException e) { - assertEquals("Failed to obtain access token after 3 attempts.", e.getMessage()); - } - - verify(mockHttpClient, times(1)).start(); - verify(mockHttpClient, times(3)).execute(any(), any(FutureCallback.class)); - } - - @Test - public void testCancelledScenario() throws Exception { - - ArgumentCaptor> captor = ArgumentCaptor.forClass(FutureCallback.class); - doNothing().when(mockHttpClient).start(); - doAnswer(invocation -> { - FutureCallback callback = captor.getValue(); - callback.cancelled(); - return null; - }).when(mockHttpClient).execute(any(), captor.capture()); - - try { - helper.requestAccessToken(); - } catch (AIServerException e) { - assertEquals("Failed to obtain access token after 3 attempts.", e.getMessage()); - } - - verify(mockHttpClient, times(1)).start(); - verify(mockHttpClient, times(3)).execute(any(), any(FutureCallback.class)); - } - - @Test(expectedExceptions = AIServerException.class) - public void testRequestAccessToken_IOException() throws Exception { - - CloseableHttpAsyncClient mockClient = mock(CloseableHttpAsyncClient.class); - doThrow(new IOException("Test IOException")).when(mockClient).close(); - - AIAccessTokenManager.AccessTokenRequestHelper helper = - new AIAccessTokenManager.AccessTokenRequestHelper("key", "endpoint", mockClient); - - helper.requestAccessToken(); - } - - private void setupMockHttpResponse(int statusCode, String responseBody) throws Exception { - - when(mockHttpClient.execute(any(HttpPost.class), any(FutureCallback.class))).thenAnswer(invocation -> { - FutureCallback callback = invocation.getArgument(1); - when(mockResponse.getStatusLine()).thenReturn(mockStatusLine); - when(mockStatusLine.getStatusCode()).thenReturn(statusCode); - when(mockResponse.getEntity()).thenReturn(new StringEntity(responseBody)); - callback.completed(mockResponse); - return mockFuture; - }); - when(mockFuture.get()).thenReturn(mockResponse); - } - - // Custom AccessTokenRequestHelper for testing. - private class TestAccessTokenRequestHelper extends AIAccessTokenManager.AccessTokenRequestHelper { - public TestAccessTokenRequestHelper(CloseableHttpAsyncClient client) { - super("testKey", "https://test.endpoint", client); - } - - @Override - public String requestAccessToken() throws AIServerException { - try { - return super.requestAccessToken(); - } catch (AIServerException e) { - // Rethrow AIServerException directly for testing purposes. - throw e; - } catch (Exception e) { - throw new AIServerException("Test exception", e); - } - } - } - - private static void assignAIKey(String key) throws NoSuchFieldException, IllegalAccessException { - - // Target class and field. - Class targetClass = AIAccessTokenManager.class; - Field aiKeyField = targetClass.getDeclaredField("AI_KEY"); - - // Make the field accessible. - aiKeyField.setAccessible(true); - - // Remove the "final" modifier. - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(aiKeyField, aiKeyField.getModifiers() & ~Modifier.FINAL); - - // Set the new value. - aiKeyField.set(null, key); // null because it's a static field. - } -} diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/conf/carbon.xml b/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/conf/carbon.xml deleted file mode 100755 index e2f7f02dc050..000000000000 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/conf/carbon.xml +++ /dev/null @@ -1,686 +0,0 @@ - - - - - - - - WSO2 Identity Server - - - IS - - - 5.3.0 - - - localhost - - - localhost - - - local:/${carbon.context}/services/ - - - - - - - IdentityServer - - - - - - - org.wso2.carbon - - - / - - - - - - - - - 15 - - - - - - - - - 0 - - - - - 9999 - - 11111 - - - - - - 10389 - - 8000 - - - - - - 10500 - - - - - - - - - org.wso2.carbon.tomcat.jndi.CarbonJavaURLContextFactory - - - - - - - - - java - - - - - - - - - - false - - - false - - - 600 - - - - false - - - - - - - - 30 - - - - - - - - - 15 - - - - - - ${carbon.home}/repository/deployment/server/ - - - 15 - - - ${carbon.home}/repository/conf/axis2/axis2.xml - - - 30000 - - - ${carbon.home}/repository/deployment/client/ - - ${carbon.home}/repository/conf/axis2/axis2_client.xml - - true - - - - - - - - - - admin - Default Administrator Role - - - user - Default User Role - - - - - - - - - - - - ${carbon.home}/repository/resources/security/wso2carbon.jks - - JKS - - wso2carbon - - wso2carbon - - wso2carbon - - - - - - ${carbon.home}/repository/resources/security/client-truststore.jks - - JKS - - wso2carbon - - - - - - - - - - - - - - - - - - - UserManager - - - false - - org.wso2.carbon.identity.provider.AttributeCallbackHandler - - - org.wso2.carbon.identity.sts.store.DBTokenStore - - - true - allow - - - - - - -claim_mgt_menu -identity_mgt_emailtemplate_menu -identity_security_questions_menu - - - - ${carbon.home}/tmp/work - - - - - - true - - - 10 - - - 30 - - - - - - 100 - - - - keystore - certificate - * - - org.wso2.carbon.ui.transports.fileupload.AnyFileUploadExecutor - - - - - jarZip - - org.wso2.carbon.ui.transports.fileupload.JarZipUploadExecutor - - - - dbs - - org.wso2.carbon.ui.transports.fileupload.DBSFileUploadExecutor - - - - tools - - org.wso2.carbon.ui.transports.fileupload.ToolsFileUploadExecutor - - - - toolsAny - - org.wso2.carbon.ui.transports.fileupload.ToolsAnyFileUploadExecutor - - - - - - - - - - info - org.wso2.carbon.core.transports.util.InfoProcessor - - - wsdl - org.wso2.carbon.core.transports.util.Wsdl11Processor - - - wsdl2 - org.wso2.carbon.core.transports.util.Wsdl20Processor - - - xsd - org.wso2.carbon.core.transports.util.XsdProcessor - - - - - - false - false - true - svn - http://svnrepo.example.com/repos/ - username - password - true - - - - - - - - - - - - - - - ${require.carbon.servlet} - - - - - true - - - - - - - default repository - http://product-dist.wso2.com/p2/carbon/releases/wilkes/ - - - - - - - - true - - - - - - true - - diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/conf/identity/identity.xml b/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/conf/identity/identity.xml deleted file mode 100644 index 1bf7cb28de20..000000000000 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/conf/identity/identity.xml +++ /dev/null @@ -1,994 +0,0 @@ - - - - - - - - - jdbc/WSO2IdentityDB - - - - - true - true - 0 - - true - 20160 - 1140 - - - 50000 - - - true - - - - true - - 20 - - 40 - - - - - - - 15 - 20160 - - - - - - ${carbon.home}/conf/keystores - SunX509 - SunX509 - - - - localhost - - - SelfAndManaged - CertValidate - - - - - - - - - - - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/openidserver - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/openid - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/openid_login.do - - false - - 7200 - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth/request-token - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth/authorize-url - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth/access-token - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/authorize - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/token - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/revoke - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/introspect - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/userinfo - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oidc/checksession - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oidc/logout - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_authz.do - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_error.do - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_consent.do - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_logout_consent.do - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_logout.do - - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/.well-known/webfinger - - - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/api/identity/oauth2/dcr/v1.1/register - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/jwks - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/oidcdiscovery - - - 300 - - 3600 - - 3600 - - 84600 - - 0 - - true - - org.wso2.carbon.identity.oauth.tokenprocessor.PlainTextPersistenceProcessor - - false - - false - - - - token - org.wso2.carbon.identity.oauth2.authz.handlers.AccessTokenResponseTypeHandler - - - code - org.wso2.carbon.identity.oauth2.authz.handlers.CodeResponseTypeHandler - - - id_token - org.wso2.carbon.identity.oauth2.authz.handlers.IDTokenResponseTypeHandler - - - id_token token - org.wso2.carbon.identity.oauth2.authz.handlers.IDTokenTokenResponseTypeHandler - - - - - - authorization_code - org.wso2.carbon.identity.oauth2.token.handlers.grant.AuthorizationCodeGrantHandler - - - password - org.wso2.carbon.identity.oauth2.token.handlers.grant.PasswordGrantHandler - - - refresh_token - org.wso2.carbon.identity.oauth2.token.handlers.grant.RefreshGrantHandler - - - client_credentials - org.wso2.carbon.identity.oauth2.token.handlers.grant.ClientCredentialsGrantHandler - false - false - - - urn:ietf:params:oauth:grant-type:saml2-bearer - org.wso2.carbon.identity.oauth2.token.handlers.grant.saml.SAML2BearerGrantHandler - - - iwa:ntlm - org.wso2.carbon.identity.oauth2.token.handlers.grant.iwa.ntlm.NTLMAuthenticationGrantHandler - - - urn:ietf:params:oauth:grant-type:jwt-bearer - org.wso2.carbon.identity.oauth2.grant.jwt.JWTBearerGrantHandler - org.wso2.carbon.identity.oauth2.grant.jwt.JWTGrantValidator - - - - - - - authorization_code - - - implicit - - - - - - - - - - - - - - - - - - - - - - - - - false - - - - - - - - false - - - - false - org.wso2.carbon.identity.oauth2.authcontext.JWTTokenGenerator - org.wso2.carbon.identity.oauth2.authcontext.DefaultClaimsRetriever - http://wso2.org/claims - SHA256withRSA - 15 - - - - - - - FEDERATED - - - - - org.wso2.carbon.identity.openidconnect.DefaultIDTokenBuilder - SHA256withRSA - - - RSA-OAEP - - A128GCM - - - - RSA1_5 - RSA-OAEP - - - A128GCM - A192GCM - A256GCM - A128CBC-HS256 - A128CBC+HS256 - - - true - - - - - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/token - org.wso2.carbon.identity.openidconnect.SAMLAssertionClaimsCallback - 3600 - SHA256withRSA - org.wso2.carbon.identity.oauth.endpoint.user.impl.UserInfoUserStoreClaimRetriever - org.wso2.carbon.identity.oauth.endpoint.user.impl.UserInforRequestDefaultValidator - org.wso2.carbon.identity.oauth.endpoint.user.impl.UserInfoISAccessTokenValidator - org.wso2.carbon.identity.oauth.endpoint.user.impl.UserInfoJSONResponseBuilder - false - - false - - 120 - - - - - request_param_value_builder - org.wso2.carbon.identity.openidconnect.RequestParamRequestObjectBuilder - - - - - org.wso2.carbon.identity.openidconnect.RequestObjectValidatorImpl - - - - - true - 0 - 5 - - - - - - - - gtalk - talk.google.com - 5222 - gmail.com - multifactor1@gmail.com - wso2carbon - - - - - - ${carbon.host} - - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/samlsso - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/samlsso_logout.do - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/samlsso_notification.do - 5 - 60000 - - false - http://wso2.org/claims - - org.wso2.carbon.identity.sso.saml.builders.assertion.DefaultSAMLAssertionBuilder - org.wso2.carbon.identity.sso.saml.builders.encryption.DefaultSSOEncrypter - org.wso2.carbon.identity.sso.saml.builders.signature.DefaultSSOSigner - org.wso2.carbon.identity.sso.saml.validators.SAML2HTTPRedirectDeflateSignatureValidator - - - - 5 - false - http://www.w3.org/2000/09/xmldsig#rsa-sha1 - http://www.w3.org/2000/09/xmldsig#sha1 - http://www.w3.org/2001/04/xmlenc#aes256-cbc - http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p - true - - - - - true - - - - - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/services/wso2carbon-sts - - - - - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/passivests - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/retry.do - org.wso2.carbon.identity.sts.passive.utils.NoPersistenceTokenStore - true - - - - - false - ${Ports.ThriftEntitlementReceivePort} - 10000 - - ${carbon.home}/repository/resources/security/wso2carbon.jks - wso2carbon - - - ${carbon.host} - - - - - - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/wso2/scim/Users - ${carbon.protocol}://${carbon.host}:${carbon.management.port}/wso2/scim/Groups - - - 5 - - - 10 - local://services - - - - - - - - - - - - - - - - - false - - true - true - - - true - - - - - - - - - - org.wso2.carbon.identity.governance.store.JDBCIdentityDataStore - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /permission/admin/manage/identity/identitymgt - - - - - - /permission/admin/manage/identity/usermgt/view - - - /permission/admin/manage/identity/usermgt/view - - - - /permission/admin/manage/identity/configmgt/list - - - - /permission/admin/manage/identity/configmgt/add - - - /permission/admin/manage/identity/configmgt/update - - - - /permission/admin/manage/identity/configmgt/delete - - - - /permission/admin/manage/identity/configmgt/add - - - /permission/admin/manage/identity/configmgt/update - - - - /permission/admin/manage/identity/configmgt/delete - - - - /permission/admin/manage/identity/configmgt/add - - - /permission/admin/manage/identity/configmgt/update - - - - /permission/admin/manage/identity/configmgt/delete - - - - - - - /permission/admin/manage/identity/consentmgt/add - - - - /permission/admin/manage/identity/consentmgt/delete - - - - /permission/admin/manage/identity/consentmgt/add - - - - /permission/admin/manage/identity/consentmgt/delete - - - - /permission/admin/manage/identity/consentmgt/add - - - - /permission/admin/manage/identity/consentmgt/delete - - - - /permission/admin/manage/identity/identitymgt - - - - /permission/admin/manage/identity/applicationmgt/create - - - /permission/admin/manage/identity/applicationmgt/delete - - - /permission/admin/manage/identity/applicationmgt/update - - - /permission/admin/manage/identity/applicationmgt/view - - - /permission/admin/manage/identity/applicationmgt/delete - - - /permission/admin/manage/identity/applicationmgt/create - - - /permission/admin/manage/identity/applicationmgt/view - - - /permission/admin/manage/identity/pep - - - /permission/admin/manage/identity/usermgt/create - - - /permission/admin/manage/identity/usermgt/list - - - /permission/admin/manage/identity/rolemgt/create - - - /permission/admin/manage/identity/rolemgt/view - - - /permission/admin/manage/identity/usermgt/view - - - /permission/admin/manage/identity/usermgt/update - - - /permission/admin/manage/identity/usermgt/update - - - /permission/admin/manage/identity/usermgt/delete - - - /permission/admin/manage/identity/rolemgt/view - - - /permission/admin/manage/identity/rolemgt/update - - - /permission/admin/manage/identity/rolemgt/update - - - /permission/admin/manage/identity/rolemgt/delete - - - /permission/admin/login - - - /permission/admin/manage/identity/usermgt/delete - - - /permission/admin/login - - - /permission/admin/login - - - /permission/admin/manage/identity/usermgt/create - - - - - - - - - /permission/admin/manage/identity/usermgt - - - /permission/admin/manage/identity/applicationmgt - - - - - - - /permission/admin/manage/identity/usermgt/update - - - - - - /permission/admin/manage/humantask/viewtasks - - - /permission/admin/login - - - /permission/admin/manage/identity/usermgt - - - /permission/admin/manage/identity/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /api/identity/user/v1.0/ - /api/identity/consent-mgt/v1.0/ - /api/identity/recovery/v0.9/ - /oauth2/ - /scim2/ - /api/identity/entitlement/ - /api/identity/oauth2/dcr/v1.1/ - - - /identity/(.*) - - - - - 300 - - - - true - - 1000 - 1000 - 51200 - - - - - - http://localhost:8280/ - - 5 - - - - - - - - - - diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/dbscripts/identity.sql b/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/dbscripts/identity.sql deleted file mode 100644 index ff1e7a44b2d4..000000000000 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/dbscripts/identity.sql +++ /dev/null @@ -1,1241 +0,0 @@ -CREATE TABLE IF NOT EXISTS IDN_BASE_TABLE ( - PRODUCT_NAME VARCHAR (20), - PRIMARY KEY (PRODUCT_NAME) -); - -INSERT INTO IDN_BASE_TABLE values ('WSO2 Identity Server'); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH_CONSUMER_APPS ( - ID INTEGER NOT NULL AUTO_INCREMENT, - CONSUMER_KEY VARCHAR (255), - CONSUMER_SECRET VARCHAR (2048), - USERNAME VARCHAR (255), - TENANT_ID INTEGER DEFAULT 0, - USER_DOMAIN VARCHAR(50), - APP_NAME VARCHAR (255), - OAUTH_VERSION VARCHAR (128), - CALLBACK_URL VARCHAR (2048), - GRANT_TYPES VARCHAR (1024), - PKCE_MANDATORY CHAR(1) DEFAULT '0', - PKCE_SUPPORT_PLAIN CHAR(1) DEFAULT '0', - APP_STATE VARCHAR (25) DEFAULT 'ACTIVE', - USER_ACCESS_TOKEN_EXPIRE_TIME BIGINT DEFAULT 3600, - APP_ACCESS_TOKEN_EXPIRE_TIME BIGINT DEFAULT 3600, - REFRESH_TOKEN_EXPIRE_TIME BIGINT DEFAULT 84600, - ID_TOKEN_EXPIRE_TIME BIGINT DEFAULT 3600, - CONSTRAINT CONSUMER_KEY_CONSTRAINT UNIQUE (TENANT_ID, CONSUMER_KEY), - PRIMARY KEY (ID) -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_SCOPE_VALIDATORS ( - APP_ID INTEGER NOT NULL, - SCOPE_VALIDATOR VARCHAR (128) NOT NULL, - PRIMARY KEY (APP_ID,SCOPE_VALIDATOR), - FOREIGN KEY (APP_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH1A_REQUEST_TOKEN ( - REQUEST_TOKEN VARCHAR (512), - REQUEST_TOKEN_SECRET VARCHAR (512), - CONSUMER_KEY_ID INTEGER, - CALLBACK_URL VARCHAR (2048), - SCOPE VARCHAR(2048), - AUTHORIZED VARCHAR (128), - OAUTH_VERIFIER VARCHAR (512), - AUTHZ_USER VARCHAR (512), - TENANT_ID INTEGER DEFAULT -1, - PRIMARY KEY (REQUEST_TOKEN), - FOREIGN KEY (CONSUMER_KEY_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH1A_ACCESS_TOKEN ( - ACCESS_TOKEN VARCHAR (512), - ACCESS_TOKEN_SECRET VARCHAR (512), - CONSUMER_KEY_ID INTEGER, - SCOPE VARCHAR(2048), - AUTHZ_USER VARCHAR (512), - TENANT_ID INTEGER DEFAULT -1, - PRIMARY KEY (ACCESS_TOKEN), - FOREIGN KEY (CONSUMER_KEY_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_ACCESS_TOKEN ( - TOKEN_ID VARCHAR (255), - ACCESS_TOKEN VARCHAR (2048), - REFRESH_TOKEN VARCHAR (2048), - CONSUMER_KEY_ID INTEGER, - AUTHZ_USER VARCHAR (100), - TENANT_ID INTEGER, - USER_DOMAIN VARCHAR(50), - USER_TYPE VARCHAR (25), - GRANT_TYPE VARCHAR (50), - TIME_CREATED TIMESTAMP DEFAULT 0, - REFRESH_TOKEN_TIME_CREATED TIMESTAMP DEFAULT 0, - VALIDITY_PERIOD BIGINT, - REFRESH_TOKEN_VALIDITY_PERIOD BIGINT, - TOKEN_SCOPE_HASH VARCHAR (32), - TOKEN_STATE VARCHAR (25) DEFAULT 'ACTIVE', - TOKEN_STATE_ID VARCHAR (128) DEFAULT 'NONE', - SUBJECT_IDENTIFIER VARCHAR(255), - ACCESS_TOKEN_HASH VARCHAR (512), - REFRESH_TOKEN_HASH VARCHAR (512), - IDP_ID INTEGER DEFAULT -1 NOT NULL, - TOKEN_BINDING_REF VARCHAR (32) DEFAULT 'NONE', - PRIMARY KEY (TOKEN_ID), - FOREIGN KEY (CONSUMER_KEY_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE, - CONSTRAINT CON_APP_KEY UNIQUE (CONSUMER_KEY_ID,AUTHZ_USER,TENANT_ID,USER_DOMAIN,USER_TYPE,TOKEN_SCOPE_HASH, - TOKEN_STATE,TOKEN_STATE_ID,IDP_ID,TOKEN_BINDING_REF) -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_TOKEN_BINDING ( - TOKEN_ID VARCHAR (255), - TOKEN_BINDING_TYPE VARCHAR (32), - TOKEN_BINDING_REF VARCHAR (32), - TOKEN_BINDING_VALUE VARCHAR (1024), - TENANT_ID INTEGER DEFAULT -1, - PRIMARY KEY (TOKEN_ID), - FOREIGN KEY (TOKEN_ID) REFERENCES IDN_OAUTH2_ACCESS_TOKEN(TOKEN_ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_ACCESS_TOKEN_AUDIT ( - TOKEN_ID VARCHAR (255), - ACCESS_TOKEN VARCHAR(2048), - REFRESH_TOKEN VARCHAR(2048), - CONSUMER_KEY_ID INTEGER, - AUTHZ_USER VARCHAR (100), - TENANT_ID INTEGER, - USER_DOMAIN VARCHAR(50), - USER_TYPE VARCHAR (25), - GRANT_TYPE VARCHAR (50), - TIME_CREATED TIMESTAMP NULL, - REFRESH_TOKEN_TIME_CREATED TIMESTAMP NULL, - VALIDITY_PERIOD BIGINT, - REFRESH_TOKEN_VALIDITY_PERIOD BIGINT, - TOKEN_SCOPE_HASH VARCHAR(32), - TOKEN_STATE VARCHAR(25), - TOKEN_STATE_ID VARCHAR (128) , - SUBJECT_IDENTIFIER VARCHAR(255), - ACCESS_TOKEN_HASH VARCHAR(512), - REFRESH_TOKEN_HASH VARCHAR(512), - INVALIDATED_TIME TIMESTAMP NULL, - IDP_ID INTEGER DEFAULT -1 NOT NULL -); - - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_AUTHORIZATION_CODE ( - CODE_ID VARCHAR (255), - AUTHORIZATION_CODE VARCHAR (2048), - CONSUMER_KEY_ID INTEGER, - CALLBACK_URL VARCHAR (2048), - SCOPE VARCHAR(2048), - AUTHZ_USER VARCHAR (100), - TENANT_ID INTEGER, - USER_DOMAIN VARCHAR(50), - TIME_CREATED TIMESTAMP, - VALIDITY_PERIOD BIGINT, - STATE VARCHAR (25) DEFAULT 'ACTIVE', - TOKEN_ID VARCHAR(255), - SUBJECT_IDENTIFIER VARCHAR(255), - PKCE_CODE_CHALLENGE VARCHAR (255), - PKCE_CODE_CHALLENGE_METHOD VARCHAR(128), - AUTHORIZATION_CODE_HASH VARCHAR (512), - IDP_ID INTEGER DEFAULT -1 NOT NULL, - PRIMARY KEY (CODE_ID), - FOREIGN KEY (CONSUMER_KEY_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_AUTHZ_CODE_SCOPE( - CODE_ID VARCHAR(255), - SCOPE VARCHAR(60), - TENANT_ID INTEGER DEFAULT -1, - PRIMARY KEY (CODE_ID, SCOPE), - FOREIGN KEY (CODE_ID) REFERENCES IDN_OAUTH2_AUTHORIZATION_CODE (CODE_ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_DEVICE_FLOW ( - CODE_ID VARCHAR(255), - DEVICE_CODE VARCHAR(255), - USER_CODE VARCHAR(25), - CONSUMER_KEY_ID INTEGER, - LAST_POLL_TIME TIMESTAMP NOT NULL, - EXPIRY_TIME TIMESTAMP NOT NULL, - TIME_CREATED TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - POLL_TIME BIGINT, - STATUS VARCHAR (25) DEFAULT 'PENDING', - AUTHZ_USER VARCHAR (100), - TENANT_ID INTEGER, - USER_DOMAIN VARCHAR(50), - IDP_ID INTEGER, - SUBJECT_IDENTIFIER VARCHAR(255), - PRIMARY KEY (DEVICE_CODE), - UNIQUE (CODE_ID), - UNIQUE (USER_CODE), - FOREIGN KEY (CONSUMER_KEY_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_DEVICE_FLOW_SCOPES ( - ID INTEGER NOT NULL AUTO_INCREMENT, - SCOPE_ID VARCHAR(255), - SCOPE VARCHAR(255), - PRIMARY KEY (ID), - FOREIGN KEY (SCOPE_ID) REFERENCES IDN_OAUTH2_DEVICE_FLOW(CODE_ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_ACCESS_TOKEN_SCOPE ( - TOKEN_ID VARCHAR (255), - TOKEN_SCOPE VARCHAR (60), - TENANT_ID INTEGER DEFAULT -1, - PRIMARY KEY (TOKEN_ID, TOKEN_SCOPE), - FOREIGN KEY (TOKEN_ID) REFERENCES IDN_OAUTH2_ACCESS_TOKEN(TOKEN_ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_SCOPE ( - SCOPE_ID INTEGER NOT NULL AUTO_INCREMENT, - NAME VARCHAR(255) NOT NULL, - DISPLAY_NAME VARCHAR(255) NOT NULL, - DESCRIPTION VARCHAR(512), - TENANT_ID INTEGER NOT NULL DEFAULT -1, - SCOPE_TYPE VARCHAR(255) NOT NULL, - PRIMARY KEY (SCOPE_ID), - UNIQUE (NAME, TENANT_ID) -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_SCOPE_BINDING ( - SCOPE_ID INTEGER NOT NULL, - SCOPE_BINDING VARCHAR(255) NOT NULL, - BINDING_TYPE VARCHAR(255) NOT NULL, - FOREIGN KEY (SCOPE_ID) REFERENCES IDN_OAUTH2_SCOPE(SCOPE_ID) ON DELETE CASCADE, - UNIQUE (SCOPE_ID, SCOPE_BINDING, BINDING_TYPE) -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_RESOURCE_SCOPE ( - RESOURCE_PATH VARCHAR(255) NOT NULL, - SCOPE_ID INTEGER NOT NULL, - TENANT_ID INTEGER DEFAULT -1, - PRIMARY KEY (RESOURCE_PATH), - FOREIGN KEY (SCOPE_ID) REFERENCES IDN_OAUTH2_SCOPE (SCOPE_ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_SCIM_GROUP ( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - ROLE_NAME VARCHAR(255) NOT NULL, - ATTR_NAME VARCHAR(1024) NOT NULL, - ATTR_VALUE VARCHAR(1024), - PRIMARY KEY (ID) -); - - - -CREATE TABLE IF NOT EXISTS IDN_OPENID_REMEMBER_ME ( - USER_NAME VARCHAR(255) NOT NULL, - TENANT_ID INTEGER DEFAULT 0, - COOKIE_VALUE VARCHAR(1024), - CREATED_TIME TIMESTAMP, - PRIMARY KEY (USER_NAME, TENANT_ID) -); - -CREATE TABLE IF NOT EXISTS IDN_OPENID_USER_RPS ( - USER_NAME VARCHAR(255) NOT NULL, - TENANT_ID INTEGER DEFAULT 0, - RP_URL VARCHAR(255) NOT NULL, - TRUSTED_ALWAYS VARCHAR(128) DEFAULT 'FALSE', - LAST_VISIT DATE NOT NULL, - VISIT_COUNT INTEGER DEFAULT 0, - DEFAULT_PROFILE_NAME VARCHAR(255) DEFAULT 'DEFAULT', - PRIMARY KEY (USER_NAME, TENANT_ID, RP_URL) -); - -CREATE TABLE IF NOT EXISTS IDN_OPENID_ASSOCIATIONS ( - HANDLE VARCHAR(255) NOT NULL, - ASSOC_TYPE VARCHAR(255) NOT NULL, - EXPIRE_IN TIMESTAMP NOT NULL, - MAC_KEY VARCHAR(255) NOT NULL, - ASSOC_STORE VARCHAR(128) DEFAULT 'SHARED', - TENANT_ID INTEGER DEFAULT -1, - PRIMARY KEY (HANDLE) -); - -CREATE TABLE IDN_STS_STORE ( - ID INTEGER AUTO_INCREMENT, - TOKEN_ID VARCHAR(255) NOT NULL, - TOKEN_CONTENT BLOB(1024) NOT NULL, - CREATE_DATE TIMESTAMP NOT NULL, - EXPIRE_DATE TIMESTAMP NOT NULL, - STATE INTEGER DEFAULT 0, - PRIMARY KEY (ID) -); - -CREATE TABLE IDN_IDENTITY_USER_DATA ( - TENANT_ID INTEGER DEFAULT -1234, - USER_NAME VARCHAR(255) NOT NULL, - DATA_KEY VARCHAR(255) NOT NULL, - DATA_VALUE VARCHAR(2048), - PRIMARY KEY (TENANT_ID, USER_NAME, DATA_KEY) -); - -CREATE TABLE IDN_IDENTITY_META_DATA ( - USER_NAME VARCHAR(255) NOT NULL, - TENANT_ID INTEGER DEFAULT -1234, - METADATA_TYPE VARCHAR(255) NOT NULL, - METADATA VARCHAR(255) NOT NULL, - VALID VARCHAR(255) NOT NULL, - PRIMARY KEY (TENANT_ID, USER_NAME, METADATA_TYPE,METADATA) -); - -CREATE TABLE IF NOT EXISTS IDN_THRIFT_SESSION ( - SESSION_ID VARCHAR(255) NOT NULL, - USER_NAME VARCHAR(255) NOT NULL, - CREATED_TIME VARCHAR(255) NOT NULL, - LAST_MODIFIED_TIME VARCHAR(255) NOT NULL, - TENANT_ID INTEGER DEFAULT -1, - PRIMARY KEY (SESSION_ID) -); - -CREATE TABLE IDN_AUTH_SESSION_STORE ( - SESSION_ID VARCHAR (100) NOT NULL, - SESSION_TYPE VARCHAR(100) NOT NULL, - OPERATION VARCHAR(10) NOT NULL, - SESSION_OBJECT BLOB, - TIME_CREATED BIGINT, - TENANT_ID INTEGER DEFAULT -1, - EXPIRY_TIME BIGINT, - PRIMARY KEY (SESSION_ID, SESSION_TYPE, TIME_CREATED, OPERATION) -); - - -CREATE TABLE IDN_AUTH_TEMP_SESSION_STORE ( - SESSION_ID VARCHAR (100) NOT NULL, - SESSION_TYPE VARCHAR(100) NOT NULL, - OPERATION VARCHAR(10) NOT NULL, - SESSION_OBJECT BLOB, - TIME_CREATED BIGINT, - TENANT_ID INTEGER DEFAULT -1, - EXPIRY_TIME BIGINT, - PRIMARY KEY (SESSION_ID, SESSION_TYPE, TIME_CREATED, OPERATION) -); - -CREATE TABLE IF NOT EXISTS IDN_AUTH_USER ( - USER_ID VARCHAR(255) NOT NULL, - USER_NAME VARCHAR(255) NOT NULL, - TENANT_ID INTEGER NOT NULL, - DOMAIN_NAME VARCHAR(255) NOT NULL, - IDP_ID INTEGER NOT NULL, - PRIMARY KEY (USER_ID), - CONSTRAINT USER_STORE_CONSTRAINT UNIQUE (USER_NAME, TENANT_ID, DOMAIN_NAME, IDP_ID)); - -CREATE TABLE IF NOT EXISTS IDN_AUTH_USER_SESSION_MAPPING ( - USER_ID VARCHAR(255) NOT NULL, - SESSION_ID VARCHAR(255) NOT NULL, - CONSTRAINT USER_SESSION_STORE_CONSTRAINT UNIQUE (USER_ID, SESSION_ID)); - -CREATE TABLE IF NOT EXISTS IDN_AUTH_SESSION_APP_INFO ( - SESSION_ID VARCHAR (100) NOT NULL, - SUBJECT VARCHAR (100) NOT NULL, - APP_ID INTEGER NOT NULL, - INBOUND_AUTH_TYPE VARCHAR (255) NOT NULL, - PRIMARY KEY (SESSION_ID, SUBJECT, APP_ID, INBOUND_AUTH_TYPE)); - -CREATE TABLE IF NOT EXISTS IDN_AUTH_SESSION_META_DATA ( - SESSION_ID VARCHAR (100) NOT NULL, - PROPERTY_TYPE VARCHAR (100) NOT NULL, - `VALUE` VARCHAR (255) NOT NULL, - PRIMARY KEY (SESSION_ID, PROPERTY_TYPE, `VALUE`) - ); - -CREATE TABLE IF NOT EXISTS SP_APP ( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - APP_NAME VARCHAR (255) NOT NULL , - USER_STORE VARCHAR (255) NOT NULL, - USERNAME VARCHAR (255) NOT NULL , - DESCRIPTION VARCHAR (1024), - ROLE_CLAIM VARCHAR (512), - AUTH_TYPE VARCHAR (255) NOT NULL, - PROVISIONING_USERSTORE_DOMAIN VARCHAR (512), - IS_LOCAL_CLAIM_DIALECT CHAR(1) DEFAULT '1', - IS_SEND_LOCAL_SUBJECT_ID CHAR(1) DEFAULT '0', - IS_SEND_AUTH_LIST_OF_IDPS CHAR(1) DEFAULT '0', - IS_USE_TENANT_DOMAIN_SUBJECT CHAR(1) DEFAULT '1', - IS_USE_USER_DOMAIN_SUBJECT CHAR(1) DEFAULT '1', - ENABLE_AUTHORIZATION CHAR(1) DEFAULT '0', - SUBJECT_CLAIM_URI VARCHAR (512), - IS_SAAS_APP CHAR(1) DEFAULT '0', - IS_DUMB_MODE CHAR(1) DEFAULT '0', - UUID CHAR(36), - IMAGE_URL VARCHAR(1024), - ACCESS_URL VARCHAR(1024), - IS_DISCOVERABLE CHAR(1) DEFAULT '0', - - PRIMARY KEY (ID)); - -ALTER TABLE SP_APP ADD CONSTRAINT APPLICATION_NAME_CONSTRAINT UNIQUE(APP_NAME, TENANT_ID); -ALTER TABLE SP_APP ADD CONSTRAINT APPLICATION_UUID_CONSTRAINT UNIQUE(UUID); - -CREATE TABLE IF NOT EXISTS SP_METADATA ( - ID INTEGER AUTO_INCREMENT, - SP_ID INTEGER, - NAME VARCHAR(255) NOT NULL, - `VALUE` VARCHAR(255) NOT NULL, - DISPLAY_NAME VARCHAR(255), - TENANT_ID INTEGER DEFAULT -1, - PRIMARY KEY (ID), - CONSTRAINT SP_METADATA_CONSTRAINT UNIQUE (SP_ID, NAME), - FOREIGN KEY (SP_ID) REFERENCES SP_APP(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS SP_INBOUND_AUTH ( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - INBOUND_AUTH_KEY VARCHAR (255), - INBOUND_AUTH_TYPE VARCHAR (255) NOT NULL, - INBOUND_CONFIG_TYPE VARCHAR (255) NOT NULL, - PROP_NAME VARCHAR (255), - PROP_VALUE VARCHAR (1024) , - APP_ID INTEGER NOT NULL, - PRIMARY KEY (ID)); - -ALTER TABLE SP_INBOUND_AUTH ADD CONSTRAINT APPLICATION_ID_CONSTRAINT FOREIGN KEY (APP_ID) REFERENCES SP_APP (ID) ON -DELETE CASCADE; - -CREATE TABLE IF NOT EXISTS SP_AUTH_STEP ( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - STEP_ORDER INTEGER DEFAULT 1, - APP_ID INTEGER NOT NULL , - IS_SUBJECT_STEP CHAR(1) DEFAULT '0', - IS_ATTRIBUTE_STEP CHAR(1) DEFAULT '0', - PRIMARY KEY (ID)); - -ALTER TABLE SP_AUTH_STEP ADD CONSTRAINT APPLICATION_ID_CONSTRAINT_STEP FOREIGN KEY (APP_ID) REFERENCES SP_APP (ID) ON -DELETE CASCADE; - -CREATE TABLE IF NOT EXISTS SP_FEDERATED_IDP ( - ID INTEGER NOT NULL, - TENANT_ID INTEGER NOT NULL, - AUTHENTICATOR_ID INTEGER NOT NULL, - PRIMARY KEY (ID, AUTHENTICATOR_ID)); - -ALTER TABLE SP_FEDERATED_IDP ADD CONSTRAINT STEP_ID_CONSTRAINT FOREIGN KEY (ID) REFERENCES SP_AUTH_STEP (ID) ON DELETE -CASCADE; - -CREATE TABLE IF NOT EXISTS SP_CLAIM_DIALECT ( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - SP_DIALECT VARCHAR (512) NOT NULL, - APP_ID INTEGER NOT NULL, - PRIMARY KEY (ID)); - -ALTER TABLE SP_CLAIM_DIALECT ADD CONSTRAINT DIALECTID_APPID_CONSTRAINT FOREIGN KEY (APP_ID) REFERENCES SP_APP (ID) ON -DELETE CASCADE; - -CREATE TABLE IF NOT EXISTS SP_CLAIM_MAPPING ( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - IDP_CLAIM VARCHAR (512) NOT NULL , - SP_CLAIM VARCHAR (512) NOT NULL , - APP_ID INTEGER NOT NULL, - IS_REQUESTED VARCHAR(128) DEFAULT '0', - IS_MANDATORY VARCHAR(128) DEFAULT '0', - DEFAULT_VALUE VARCHAR(255), - PRIMARY KEY (ID)); - -ALTER TABLE SP_CLAIM_MAPPING ADD CONSTRAINT CLAIMID_APPID_CONSTRAINT FOREIGN KEY (APP_ID) REFERENCES SP_APP (ID) ON -DELETE CASCADE; - -CREATE TABLE IF NOT EXISTS SP_ROLE_MAPPING ( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - IDP_ROLE VARCHAR (255) NOT NULL , - SP_ROLE VARCHAR (255) NOT NULL , - APP_ID INTEGER NOT NULL, - PRIMARY KEY (ID)); - -ALTER TABLE SP_ROLE_MAPPING ADD CONSTRAINT ROLEID_APPID_CONSTRAINT FOREIGN KEY (APP_ID) REFERENCES SP_APP (ID) ON -DELETE CASCADE; - -CREATE TABLE IF NOT EXISTS SP_REQ_PATH_AUTHENTICATOR ( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - AUTHENTICATOR_NAME VARCHAR (255) NOT NULL , - APP_ID INTEGER NOT NULL, - PRIMARY KEY (ID)); - -ALTER TABLE SP_REQ_PATH_AUTHENTICATOR ADD CONSTRAINT REQ_AUTH_APPID_CONSTRAINT FOREIGN KEY (APP_ID) REFERENCES SP_APP -(ID) ON DELETE CASCADE; - -CREATE TABLE IF NOT EXISTS SP_PROVISIONING_CONNECTOR ( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - IDP_NAME VARCHAR (255) NOT NULL , - CONNECTOR_NAME VARCHAR (255) NOT NULL , - APP_ID INTEGER NOT NULL, - IS_JIT_ENABLED CHAR(1) NOT NULL DEFAULT '0', - BLOCKING CHAR(1) NOT NULL DEFAULT '0', - RULE_ENABLED CHAR(1) NOT NULL DEFAULT '0', - PRIMARY KEY (ID)); - -ALTER TABLE SP_PROVISIONING_CONNECTOR ADD CONSTRAINT PRO_CONNECTOR_APPID_CONSTRAINT FOREIGN KEY (APP_ID) REFERENCES -SP_APP (ID) ON DELETE CASCADE; - -CREATE TABLE IF NOT EXISTS SP_AUTH_SCRIPT ( - ID INTEGER AUTO_INCREMENT NOT NULL, - TENANT_ID INTEGER NOT NULL, - APP_ID INTEGER NOT NULL, - TYPE VARCHAR(255) NOT NULL, - CONTENT BLOB DEFAULT NULL, - IS_ENABLED CHAR(1) NOT NULL DEFAULT '0', - PRIMARY KEY (ID)); - -CREATE TABLE SP_TEMPLATE ( - ID INTEGER AUTO_INCREMENT NOT NULL, - TENANT_ID INTEGER NOT NULL, - NAME VARCHAR(255) NOT NULL, - DESCRIPTION VARCHAR(1023), - CONTENT BLOB DEFAULT NULL, - PRIMARY KEY (ID), - CONSTRAINT SP_TEMPLATE_CONSTRAINT UNIQUE (TENANT_ID, NAME)); - -CREATE TABLE IF NOT EXISTS SP_TRUSTED_APPS ( - ID INTEGER AUTO_INCREMENT, - SP_ID INTEGER NOT NULL, - PLATFORM_TYPE VARCHAR(255) NOT NULL, - APP_IDENTIFIER VARCHAR(255) NOT NULL, - THUMBPRINTS VARCHAR(2048), - IS_FIDO_TRUSTED BOOLEAN DEFAULT FALSE, - TENANT_ID INTEGER DEFAULT -1, - PRIMARY KEY (ID), - UNIQUE (SP_ID, PLATFORM_TYPE), - FOREIGN KEY (SP_ID) REFERENCES SP_APP(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDN_AUTH_WAIT_STATUS ( - ID INTEGER AUTO_INCREMENT NOT NULL, - TENANT_ID INTEGER NOT NULL, - LONG_WAIT_KEY VARCHAR(255) NOT NULL, - WAIT_STATUS CHAR(1) NOT NULL DEFAULT '1', - TIME_CREATED TIMESTAMP DEFAULT 0, - EXPIRE_TIME TIMESTAMP DEFAULT 0, - PRIMARY KEY (ID), - CONSTRAINT IDN_AUTH_WAIT_STATUS_KEY UNIQUE (LONG_WAIT_KEY)); - -CREATE TABLE IF NOT EXISTS IDP ( - ID INTEGER AUTO_INCREMENT, - TENANT_ID INTEGER, - NAME VARCHAR(254) NOT NULL, - IS_ENABLED CHAR(1) NOT NULL DEFAULT '1', - IS_PRIMARY CHAR(1) NOT NULL DEFAULT '0', - HOME_REALM_ID VARCHAR(254), - IMAGE MEDIUMBLOB, - CERTIFICATE BLOB, - ALIAS VARCHAR(254), - INBOUND_PROV_ENABLED CHAR(1) NOT NULL DEFAULT '0', - INBOUND_PROV_USER_STORE_ID VARCHAR(254), - USER_CLAIM_URI VARCHAR(254), - ROLE_CLAIM_URI VARCHAR(254), - DESCRIPTION VARCHAR(1024), - DEFAULT_AUTHENTICATOR_NAME VARCHAR(254), - DEFAULT_PRO_CONNECTOR_NAME VARCHAR(254), - PROVISIONING_ROLE VARCHAR(128), - IS_FEDERATION_HUB CHAR(1) NOT NULL DEFAULT '0', - IS_LOCAL_CLAIM_DIALECT CHAR(1) NOT NULL DEFAULT '0', - DISPLAY_NAME VARCHAR(255), - IMAGE_URL VARCHAR(1024), - UUID CHAR(36) NOT NULL, - PRIMARY KEY (ID), - UNIQUE (TENANT_ID, NAME), - UNIQUE (UUID) -); - -CREATE TABLE IF NOT EXISTS IDP_ROLE ( - ID INTEGER AUTO_INCREMENT, - IDP_ID INTEGER, - TENANT_ID INTEGER, - ROLE VARCHAR(254), - PRIMARY KEY (ID), - UNIQUE (IDP_ID, ROLE), - FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDP_GROUP ( - ID INTEGER AUTO_INCREMENT NOT NULL, - IDP_ID INTEGER NOT NULL, - TENANT_ID INTEGER NOT NULL, - GROUP_NAME VARCHAR(255) NOT NULL, - UUID CHAR(36) NOT NULL, - PRIMARY KEY (ID), - UNIQUE (IDP_ID, GROUP_NAME), - UNIQUE (UUID), - FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDP_ROLE_MAPPING ( - ID INTEGER AUTO_INCREMENT, - IDP_ROLE_ID INTEGER, - TENANT_ID INTEGER, - USER_STORE_ID VARCHAR (253), - LOCAL_ROLE VARCHAR(253), - PRIMARY KEY (ID), - UNIQUE (IDP_ROLE_ID, TENANT_ID, USER_STORE_ID, LOCAL_ROLE), - FOREIGN KEY (IDP_ROLE_ID) REFERENCES IDP_ROLE(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDP_CLAIM ( - ID INTEGER AUTO_INCREMENT, - IDP_ID INTEGER, - TENANT_ID INTEGER, - CLAIM VARCHAR(254), - PRIMARY KEY (ID), - UNIQUE (IDP_ID, CLAIM), - FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDP_CLAIM_MAPPING ( - ID INTEGER AUTO_INCREMENT, - IDP_CLAIM_ID INTEGER, - TENANT_ID INTEGER, - LOCAL_CLAIM VARCHAR(253), - DEFAULT_VALUE VARCHAR(255), - IS_REQUESTED VARCHAR(128) DEFAULT '0', - PRIMARY KEY (ID), - UNIQUE (IDP_CLAIM_ID, TENANT_ID, LOCAL_CLAIM), - FOREIGN KEY (IDP_CLAIM_ID) REFERENCES IDP_CLAIM(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR ( - ID INTEGER AUTO_INCREMENT, - TENANT_ID INTEGER, - IDP_ID INTEGER, - NAME VARCHAR(255) NOT NULL, - IS_ENABLED CHAR (1) DEFAULT '1', - DISPLAY_NAME VARCHAR(255), - PRIMARY KEY (ID), - UNIQUE (TENANT_ID, IDP_ID, NAME), - FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDP_METADATA ( - ID INTEGER AUTO_INCREMENT, - IDP_ID INTEGER, - NAME VARCHAR(255) NOT NULL, - `VALUE` VARCHAR(255) NOT NULL, - DISPLAY_NAME VARCHAR(255), - TENANT_ID INTEGER DEFAULT -1, - PRIMARY KEY (ID), - CONSTRAINT IDP_METADATA_CONSTRAINT UNIQUE (IDP_ID, NAME), - FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR_PROPERTY ( - ID INTEGER AUTO_INCREMENT, - TENANT_ID INTEGER, - AUTHENTICATOR_ID INTEGER, - PROPERTY_KEY VARCHAR(255) NOT NULL, - PROPERTY_VALUE VARCHAR(2047), - IS_SECRET CHAR (1) DEFAULT '0', - PRIMARY KEY (ID), - UNIQUE (TENANT_ID, AUTHENTICATOR_ID, PROPERTY_KEY), - FOREIGN KEY (AUTHENTICATOR_ID) REFERENCES IDP_AUTHENTICATOR(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDP_PROVISIONING_CONFIG ( - ID INTEGER AUTO_INCREMENT, - TENANT_ID INTEGER, - IDP_ID INTEGER, - PROVISIONING_CONNECTOR_TYPE VARCHAR(255) NOT NULL, - IS_ENABLED CHAR (1) DEFAULT '0', - IS_BLOCKING CHAR (1) DEFAULT '0', - IS_RULES_ENABLED CHAR (1) DEFAULT '0', - PRIMARY KEY (ID), - UNIQUE (TENANT_ID, IDP_ID, PROVISIONING_CONNECTOR_TYPE), - FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDP_PROV_CONFIG_PROPERTY ( - ID INTEGER AUTO_INCREMENT, - TENANT_ID INTEGER, - PROVISIONING_CONFIG_ID INTEGER, - PROPERTY_KEY VARCHAR(255) NOT NULL, - PROPERTY_VALUE VARCHAR(2048), - PROPERTY_BLOB_VALUE BLOB, - PROPERTY_TYPE VARCHAR(32) NOT NULL, - IS_SECRET CHAR (1) DEFAULT '0', - PRIMARY KEY (ID), - UNIQUE (TENANT_ID, PROVISIONING_CONFIG_ID, PROPERTY_KEY), - FOREIGN KEY (PROVISIONING_CONFIG_ID) REFERENCES IDP_PROVISIONING_CONFIG(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDP_PROVISIONING_ENTITY ( - ID INTEGER AUTO_INCREMENT, - PROVISIONING_CONFIG_ID INTEGER, - ENTITY_TYPE VARCHAR(255) NOT NULL, - ENTITY_LOCAL_USERSTORE VARCHAR(255) NOT NULL, - ENTITY_NAME VARCHAR(255) NOT NULL, - ENTITY_VALUE VARCHAR(255), - TENANT_ID INTEGER, - ENTITY_LOCAL_ID VARCHAR(255), - PRIMARY KEY (ID), - UNIQUE (ENTITY_TYPE, TENANT_ID, ENTITY_LOCAL_USERSTORE, ENTITY_NAME, PROVISIONING_CONFIG_ID), - UNIQUE (PROVISIONING_CONFIG_ID, ENTITY_TYPE, ENTITY_VALUE), - FOREIGN KEY (PROVISIONING_CONFIG_ID) REFERENCES IDP_PROVISIONING_CONFIG(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDP_LOCAL_CLAIM ( - ID INTEGER AUTO_INCREMENT, - TENANT_ID INTEGER, - IDP_ID INTEGER, - CLAIM_URI VARCHAR(255) NOT NULL, - DEFAULT_VALUE VARCHAR(255), - IS_REQUESTED VARCHAR(128) DEFAULT '0', - PRIMARY KEY (ID), - UNIQUE (TENANT_ID, IDP_ID, CLAIM_URI), - FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE); - -CREATE TABLE IF NOT EXISTS IDN_ASSOCIATED_ID ( - ID INTEGER AUTO_INCREMENT, - IDP_USER_ID VARCHAR(255) NOT NULL, - TENANT_ID INTEGER DEFAULT -1234, - IDP_ID INTEGER NOT NULL, - DOMAIN_NAME VARCHAR(255) NOT NULL, - USER_NAME VARCHAR(255) NOT NULL, - ASSOCIATION_ID CHAR(36) NOT NULL, - PRIMARY KEY (ID), - UNIQUE(IDP_USER_ID, TENANT_ID, IDP_ID), - FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_USER_ACCOUNT_ASSOCIATION ( - ASSOCIATION_KEY VARCHAR(255) NOT NULL, - TENANT_ID INTEGER, - DOMAIN_NAME VARCHAR(255) NOT NULL, - USER_NAME VARCHAR(255) NOT NULL, - PRIMARY KEY (TENANT_ID, DOMAIN_NAME, USER_NAME)); - -CREATE TABLE IF NOT EXISTS FIDO_DEVICE_STORE ( - TENANT_ID INTEGER, - DOMAIN_NAME VARCHAR(255) NOT NULL, - USER_NAME VARCHAR(45) NOT NULL, - TIME_REGISTERED TIMESTAMP, - KEY_HANDLE VARCHAR(200) NOT NULL, - DEVICE_DATA VARCHAR(2048) NOT NULL, - PRIMARY KEY (TENANT_ID, DOMAIN_NAME, USER_NAME, KEY_HANDLE)); - -CREATE TABLE IF NOT EXISTS FIDO2_DEVICE_STORE ( - TENANT_ID INTEGER, - DOMAIN_NAME VARCHAR(255) NOT NULL, - USER_NAME VARCHAR(45) NOT NULL, - TIME_REGISTERED TIMESTAMP, - USER_HANDLE VARCHAR(200) NOT NULL, - CREDENTIAL_ID VARCHAR(200) NOT NULL, - PUBLIC_KEY_COSE VARCHAR(2048) NOT NULL, - SIGNATURE_COUNT BIGINT, - USER_IDENTITY VARCHAR(200) NOT NULL, - DISPLAY_NAME VARCHAR(255), - IS_USERNAMELESS_SUPPORTED CHAR(1) DEFAULT '0', - PRIMARY KEY (TENANT_ID, DOMAIN_NAME, USER_NAME, USER_HANDLE)); - -CREATE TABLE IF NOT EXISTS WF_REQUEST ( - UUID VARCHAR (45), - CREATED_BY VARCHAR (255), - TENANT_ID INTEGER DEFAULT -1, - OPERATION_TYPE VARCHAR (50), - CREATED_AT TIMESTAMP, - UPDATED_AT TIMESTAMP, - STATUS VARCHAR (30), - REQUEST BLOB, - PRIMARY KEY (UUID) -); - -CREATE TABLE IF NOT EXISTS IDN_RECOVERY_FLOW_DATA ( - RECOVERY_FLOW_ID VARCHAR(255) NOT NULL, - CODE VARCHAR(255), - FAILED_ATTEMPTS INTEGER DEFAULT 0 NOT NULL, - RESEND_COUNT INTEGER DEFAULT 0 NOT NULL, - TIME_CREATED TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY(RECOVERY_FLOW_ID) -); - -CREATE TABLE IF NOT EXISTS IDN_RECOVERY_DATA ( - USER_NAME VARCHAR(255) NOT NULL, - USER_DOMAIN VARCHAR(127) NOT NULL, - TENANT_ID INTEGER DEFAULT -1, - CODE VARCHAR(255) NOT NULL, - SCENARIO VARCHAR(255) NOT NULL, - STEP VARCHAR(127) NOT NULL, - TIME_CREATED TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - REMAINING_SETS VARCHAR(2500) DEFAULT NULL, - RECOVERY_FLOW_ID VARCHAR(255), - PRIMARY KEY(USER_NAME, USER_DOMAIN, TENANT_ID, SCENARIO,STEP), - FOREIGN KEY (RECOVERY_FLOW_ID) REFERENCES IDN_RECOVERY_FLOW_DATA(RECOVERY_FLOW_ID) ON DELETE CASCADE, - UNIQUE(CODE) -); - -CREATE TABLE IF NOT EXISTS IDN_PASSWORD_HISTORY_DATA ( - ID INTEGER NOT NULL AUTO_INCREMENT, - USER_NAME VARCHAR(255) NOT NULL, - USER_DOMAIN VARCHAR(127) NOT NULL, - TENANT_ID INTEGER DEFAULT -1, - SALT_VALUE VARCHAR(255), - HASH VARCHAR(255) NOT NULL, - TIME_CREATED TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (ID), - UNIQUE (USER_NAME,USER_DOMAIN,TENANT_ID,SALT_VALUE,HASH) -); - -CREATE TABLE IF NOT EXISTS IDN_CLAIM_DIALECT ( - ID INTEGER NOT NULL AUTO_INCREMENT, - DIALECT_URI VARCHAR (255) NOT NULL, - TENANT_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - CONSTRAINT DIALECT_URI_CONSTRAINT UNIQUE (DIALECT_URI, TENANT_ID) -); - -CREATE TABLE IF NOT EXISTS IDN_CLAIM ( - ID INTEGER NOT NULL AUTO_INCREMENT, - DIALECT_ID INTEGER NOT NULL, - CLAIM_URI VARCHAR (255) NOT NULL, - TENANT_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - FOREIGN KEY (DIALECT_ID) REFERENCES IDN_CLAIM_DIALECT(ID) ON DELETE CASCADE, - CONSTRAINT CLAIM_URI_CONSTRAINT UNIQUE (DIALECT_ID, CLAIM_URI, TENANT_ID) -); - -CREATE TABLE IF NOT EXISTS IDN_CLAIM_MAPPED_ATTRIBUTE ( - ID INTEGER NOT NULL AUTO_INCREMENT, - LOCAL_CLAIM_ID INTEGER, - USER_STORE_DOMAIN_NAME VARCHAR (255) NOT NULL, - ATTRIBUTE_NAME VARCHAR (255) NOT NULL, - TENANT_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - FOREIGN KEY (LOCAL_CLAIM_ID) REFERENCES IDN_CLAIM(ID) ON DELETE CASCADE, - CONSTRAINT USER_STORE_DOMAIN_CONSTRAINT UNIQUE (LOCAL_CLAIM_ID, USER_STORE_DOMAIN_NAME, TENANT_ID) -); - -CREATE TABLE IF NOT EXISTS IDN_CLAIM_PROPERTY ( - ID INTEGER NOT NULL AUTO_INCREMENT, - LOCAL_CLAIM_ID INTEGER, - PROPERTY_NAME VARCHAR (255) NOT NULL, - PROPERTY_VALUE VARCHAR (255) NOT NULL, - TENANT_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - FOREIGN KEY (LOCAL_CLAIM_ID) REFERENCES IDN_CLAIM(ID) ON DELETE CASCADE, - CONSTRAINT PROPERTY_NAME_CONSTRAINT UNIQUE (LOCAL_CLAIM_ID, PROPERTY_NAME, TENANT_ID) -); - -CREATE TABLE IF NOT EXISTS IDN_CLAIM_MAPPING ( - ID INTEGER NOT NULL AUTO_INCREMENT, - EXT_CLAIM_ID INTEGER NOT NULL, - MAPPED_LOCAL_CLAIM_ID INTEGER NOT NULL, - TENANT_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - FOREIGN KEY (EXT_CLAIM_ID) REFERENCES IDN_CLAIM(ID) ON DELETE CASCADE, - FOREIGN KEY (MAPPED_LOCAL_CLAIM_ID) REFERENCES IDN_CLAIM(ID) ON DELETE CASCADE, - CONSTRAINT EXT_TO_LOC_MAPPING_CONSTRN UNIQUE (EXT_CLAIM_ID, TENANT_ID) -); - -CREATE TABLE IF NOT EXISTS IDN_SAML2_ASSERTION_STORE ( - ID INTEGER NOT NULL AUTO_INCREMENT, - SAML2_ID VARCHAR(255) , - SAML2_ISSUER VARCHAR(255) , - SAML2_SUBJECT VARCHAR(255) , - SAML2_SESSION_INDEX VARCHAR(255) , - SAML2_AUTHN_CONTEXT_CLASS_REF VARCHAR(255) , - SAML2_ASSERTION VARCHAR(4096) , - ASSERTION BLOB , - PRIMARY KEY (ID) -); - -CREATE TABLE IDN_SAML2_ARTIFACT_STORE ( - ID INT NOT NULL AUTO_INCREMENT, - SOURCE_ID VARCHAR(255) NOT NULL, - MESSAGE_HANDLER VARCHAR(255) NOT NULL, - AUTHN_REQ_DTO BLOB NOT NULL, - SESSION_ID VARCHAR(255) NOT NULL, - INIT_TIMESTAMP TIMESTAMP NOT NULL, - EXP_TIMESTAMP TIMESTAMP NOT NULL, - ASSERTION_ID VARCHAR(255), - PRIMARY KEY (`ID`) -); - -CREATE TABLE IF NOT EXISTS IDN_OIDC_JTI ( - JWT_ID VARCHAR(255), - EXP_TIME TIMESTAMP NOT NULL , - TIME_CREATED TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP , - PRIMARY KEY (JWT_ID) -); - - -CREATE TABLE IF NOT EXISTS IDN_OIDC_PROPERTY ( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER, - CONSUMER_KEY VARCHAR(255) , - PROPERTY_KEY VARCHAR(255) NOT NULL, - PROPERTY_VALUE VARCHAR(2047) , - PRIMARY KEY (ID), - FOREIGN KEY (TENANT_ID, CONSUMER_KEY) REFERENCES IDN_OAUTH_CONSUMER_APPS(TENANT_ID, CONSUMER_KEY) ON DELETE CASCADE -); -CREATE TABLE IF NOT EXISTS IDN_OIDC_REQ_OBJECT_REFERENCE ( - ID INTEGER NOT NULL AUTO_INCREMENT, - CONSUMER_KEY_ID INTEGER , - CODE_ID VARCHAR(255) , - TOKEN_ID VARCHAR(255) , - SESSION_DATA_KEY VARCHAR(255), - PRIMARY KEY (ID), - FOREIGN KEY (CONSUMER_KEY_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE, - FOREIGN KEY (TOKEN_ID) REFERENCES IDN_OAUTH2_ACCESS_TOKEN(TOKEN_ID) ON DELETE CASCADE, - FOREIGN KEY (CODE_ID) REFERENCES IDN_OAUTH2_AUTHORIZATION_CODE(CODE_ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_OIDC_REQ_OBJECT_CLAIMS ( - ID INTEGER NOT NULL AUTO_INCREMENT, - REQ_OBJECT_ID INTEGER, - CLAIM_ATTRIBUTE VARCHAR(255) , - ESSENTIAL CHAR(1) NOT NULL DEFAULT '0', - `VALUE` VARCHAR(255) , - IS_USERINFO CHAR(1) NOT NULL DEFAULT '0', - PRIMARY KEY (ID), - FOREIGN KEY (REQ_OBJECT_ID) REFERENCES IDN_OIDC_REQ_OBJECT_REFERENCE (ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_OIDC_REQ_OBJ_CLAIM_VALUES ( - ID INTEGER NOT NULL AUTO_INCREMENT, - REQ_OBJECT_CLAIMS_ID INTEGER , - CLAIM_VALUES VARCHAR(255) , - PRIMARY KEY (ID), - FOREIGN KEY (REQ_OBJECT_CLAIMS_ID) REFERENCES IDN_OIDC_REQ_OBJECT_CLAIMS(ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_CERTIFICATE ( - ID INTEGER NOT NULL AUTO_INCREMENT, - NAME VARCHAR(100), - CERTIFICATE_IN_PEM BLOB, - TENANT_ID INTEGER DEFAULT 0, - PRIMARY KEY(ID), - CONSTRAINT CERTIFICATE_UNIQUE_KEY UNIQUE (NAME, TENANT_ID) -); - -CREATE TABLE IF NOT EXISTS IDN_OIDC_SCOPE_CLAIM_MAPPING ( - ID INTEGER NOT NULL AUTO_INCREMENT, - SCOPE_ID INTEGER NOT NULL, - EXTERNAL_CLAIM_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - FOREIGN KEY (SCOPE_ID) REFERENCES IDN_OAUTH2_SCOPE(SCOPE_ID) ON DELETE CASCADE, - FOREIGN KEY (EXTERNAL_CLAIM_ID) REFERENCES IDN_CLAIM(ID) ON DELETE CASCADE, - UNIQUE (SCOPE_ID, EXTERNAL_CLAIM_ID) -); - -CREATE TABLE IF NOT EXISTS IDN_FUNCTION_LIBRARY ( - NAME VARCHAR(255) NOT NULL, - DESCRIPTION VARCHAR(1023), - TYPE VARCHAR(255) NOT NULL, - TENANT_ID INTEGER NOT NULL, - DATA BLOB NOT NULL, - PRIMARY KEY (TENANT_ID,NAME) -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_CIBA_AUTH_CODE ( - AUTH_CODE_KEY CHAR (36), - AUTH_REQ_ID CHAR (36), - ISSUED_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSUMER_KEY VARCHAR(255), - LAST_POLLED_TIME TIMESTAMP NOT NULL, - POLLING_INTERVAL INTEGER, - EXPIRES_IN INTEGER, - AUTHENTICATED_USER_NAME VARCHAR(255), - USER_STORE_DOMAIN VARCHAR(100), - TENANT_ID INTEGER, - AUTH_REQ_STATUS VARCHAR (100) DEFAULT 'REQUESTED', - IDP_ID INTEGER, - UNIQUE(AUTH_REQ_ID), - PRIMARY KEY (AUTH_CODE_KEY), - FOREIGN KEY (TENANT_ID, CONSUMER_KEY) REFERENCES IDN_OAUTH_CONSUMER_APPS(TENANT_ID, CONSUMER_KEY) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_OAUTH2_CIBA_REQUEST_SCOPES ( - AUTH_CODE_KEY CHAR (36), - SCOPE VARCHAR (255), - FOREIGN KEY (AUTH_CODE_KEY) REFERENCES IDN_OAUTH2_CIBA_AUTH_CODE(AUTH_CODE_KEY) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS IDN_FED_AUTH_SESSION_MAPPING ( - IDP_SESSION_ID VARCHAR(255) NOT NULL, - SESSION_ID VARCHAR(255) NOT NULL, - IDP_NAME VARCHAR(255) NOT NULL, - AUTHENTICATOR_ID VARCHAR(255), - PROTOCOL_TYPE VARCHAR(255), - TIME_CREATED TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY(IDP_SESSION_ID) -); - -CREATE TABLE IF NOT EXISTS IDN_CONFIG_TYPE ( - ID VARCHAR(255) NOT NULL, - NAME VARCHAR(255) NOT NULL, - DESCRIPTION VARCHAR(1023) NULL, - PRIMARY KEY (ID), - CONSTRAINT TYPE_NAME_CONSTRAINT UNIQUE (NAME) -); - -INSERT INTO IDN_CONFIG_TYPE (ID, NAME, DESCRIPTION) VALUES -('9ab0ef95-13e9-4ed5-afaf-d29bed62f7bd', 'IDP_TEMPLATE', 'Template type to uniquely identify IDP templates'), -('3c4ac3d0-5903-4e3d-aaca-38df65b33bfd', 'APPLICATION_TEMPLATE', -'Template type to uniquely identify Application templates'), -('8ec6dbf1-218a-49bf-bc34-0d2db52d151c', 'CORS_CONFIGURATION', -'A resource type to keep the tenant CORS configurations'); - -CREATE TABLE IF NOT EXISTS IDN_CONFIG_RESOURCE ( - ID VARCHAR(255) NOT NULL, - TENANT_ID INT NOT NULL, - NAME VARCHAR(255) NOT NULL, - CREATED_TIME TIMESTAMP NOT NULL, - LAST_MODIFIED TIMESTAMP NOT NULL, - HAS_FILE BOOLEAN NOT NULL, - HAS_ATTRIBUTE BOOLEAN NOT NULL, - TYPE_ID VARCHAR(255) NOT NULL, - UNIQUE (NAME, TENANT_ID, TYPE_ID), - PRIMARY KEY (ID) -); -ALTER TABLE IDN_CONFIG_RESOURCE -ADD CONSTRAINT TYPE_ID_FOREIGN_CONSTRAINT FOREIGN KEY (TYPE_ID) REFERENCES IDN_CONFIG_TYPE (ID) -ON DELETE CASCADE ON UPDATE CASCADE; - -CREATE TABLE IF NOT EXISTS IDN_CONFIG_ATTRIBUTE ( - ID VARCHAR(255) NOT NULL, - RESOURCE_ID VARCHAR(255) NOT NULL, - ATTR_KEY VARCHAR(255) NOT NULL, - ATTR_VALUE VARCHAR(1023) NULL, - PRIMARY KEY (ID), - UNIQUE (RESOURCE_ID, ATTR_KEY) -); -ALTER TABLE IDN_CONFIG_ATTRIBUTE -ADD CONSTRAINT RESOURCE_ID_ATTRIBUTE_FOREIGN_CONSTRAINT FOREIGN KEY (RESOURCE_ID) REFERENCES -IDN_CONFIG_RESOURCE (ID) ON DELETE CASCADE ON UPDATE CASCADE; - -CREATE TABLE IF NOT EXISTS IDN_CONFIG_FILE ( - ID VARCHAR(255) NOT NULL, - `VALUE` BLOB NULL, - RESOURCE_ID VARCHAR(255) NOT NULL, - NAME VARCHAR(255) NULL, - PRIMARY KEY (ID) -); -ALTER TABLE IDN_CONFIG_FILE -ADD CONSTRAINT RESOURCE_ID_FILE_FOREIGN_CONSTRAINT FOREIGN KEY (RESOURCE_ID) REFERENCES -IDN_CONFIG_RESOURCE (ID) ON DELETE CASCADE ON UPDATE CASCADE; - -CREATE TABLE IF NOT EXISTS IDN_REMOTE_FETCH_CONFIG ( - ID VARCHAR(255) NOT NULL, - TENANT_ID INTEGER NOT NULL, - IS_ENABLED CHAR(1) NOT NULL, - REPO_MANAGER_TYPE VARCHAR(255) NOT NULL, - ACTION_LISTENER_TYPE VARCHAR(255) NOT NULL, - CONFIG_DEPLOYER_TYPE VARCHAR(255) NOT NULL, - REMOTE_FETCH_NAME VARCHAR(255), - REMOTE_RESOURCE_URI VARCHAR(255) NOT NULL, - ATTRIBUTES_JSON MEDIUMTEXT NOT NULL, - PRIMARY KEY (ID), - CONSTRAINT UC_REMOTE_RESOURCE_TYPE UNIQUE (TENANT_ID, CONFIG_DEPLOYER_TYPE) -); - -CREATE TABLE IF NOT EXISTS IDN_REMOTE_FETCH_REVISIONS ( - ID VARCHAR(255) NOT NULL, - CONFIG_ID VARCHAR(255) NOT NULL, - FILE_PATH VARCHAR(255) NOT NULL, - FILE_HASH VARCHAR(255), - DEPLOYED_DATE TIMESTAMP, - LAST_SYNC_TIME TIMESTAMP, - DEPLOYMENT_STATUS VARCHAR(255), - ITEM_NAME VARCHAR(255), - DEPLOY_ERR_LOG MEDIUMTEXT, - PRIMARY KEY (ID), - FOREIGN KEY (CONFIG_ID) REFERENCES IDN_REMOTE_FETCH_CONFIG(ID) ON DELETE CASCADE, - CONSTRAINT UC_REVISIONS UNIQUE (CONFIG_ID, ITEM_NAME) -); - - -CREATE TABLE IF NOT EXISTS IDN_USER_FUNCTIONALITY_MAPPING ( - ID VARCHAR(255) NOT NULL, - USER_ID VARCHAR(255) NOT NULL, - TENANT_ID INTEGER NOT NULL, - FUNCTIONALITY_ID VARCHAR(255) NOT NULL, - IS_FUNCTIONALITY_LOCKED BOOLEAN NOT NULL, - FUNCTIONALITY_UNLOCK_TIME BIGINT NOT NULL, - FUNCTIONALITY_LOCK_REASON VARCHAR(1023), - FUNCTIONALITY_LOCK_REASON_CODE VARCHAR(255), - PRIMARY KEY (ID), - CONSTRAINT IDN_USER_FUNCTIONALITY_MAPPING_CONSTRAINT UNIQUE (USER_ID, TENANT_ID, FUNCTIONALITY_ID) -); - -CREATE TABLE IF NOT EXISTS IDN_USER_FUNCTIONALITY_PROPERTY ( - ID VARCHAR(255) NOT NULL, - USER_ID VARCHAR(255) NOT NULL, - TENANT_ID INTEGER NOT NULL, - FUNCTIONALITY_ID VARCHAR(255) NOT NULL, - PROPERTY_NAME VARCHAR(255), - PROPERTY_VALUE VARCHAR(255), - PRIMARY KEY (ID), - CONSTRAINT IDN_USER_FUNCTIONALITY_PROPERTY_CONSTRAINT UNIQUE (USER_ID, TENANT_ID, FUNCTIONALITY_ID, PROPERTY_NAME) -); - -CREATE TABLE IF NOT EXISTS IDN_CORS_ORIGIN ( - ID INT NOT NULL AUTO_INCREMENT, - TENANT_ID INT NOT NULL, - ORIGIN VARCHAR(2048) NOT NULL, - UUID CHAR(36) NOT NULL, - - PRIMARY KEY (ID), - UNIQUE (TENANT_ID, ORIGIN), - UNIQUE (UUID) -); - -CREATE TABLE IF NOT EXISTS IDN_CORS_ASSOCIATION ( - IDN_CORS_ORIGIN_ID INT NOT NULL, - SP_APP_ID INT NOT NULL, - - PRIMARY KEY (IDN_CORS_ORIGIN_ID, SP_APP_ID), - FOREIGN KEY (IDN_CORS_ORIGIN_ID) REFERENCES IDN_CORS_ORIGIN (ID) ON DELETE CASCADE, - FOREIGN KEY (SP_APP_ID) REFERENCES SP_APP (ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS SP_SHARED_APP ( - ID INTEGER NOT NULL AUTO_INCREMENT, - MAIN_APP_ID CHAR(36) NOT NULL, - OWNER_ORG_ID CHAR(36) NOT NULL, - SHARED_APP_ID CHAR(36) NOT NULL, - SHARED_ORG_ID CHAR(36) NOT NULL, - SHARE_WITH_ALL_CHILDREN BOOLEAN DEFAULT FALSE, - PRIMARY KEY (ID), - FOREIGN KEY (MAIN_APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, - FOREIGN KEY (SHARED_APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, - UNIQUE (MAIN_APP_ID, OWNER_ORG_ID, SHARED_ORG_ID), - UNIQUE (SHARED_APP_ID) -); - -CREATE TABLE IF NOT EXISTS API_RESOURCE ( - ID VARCHAR(255) NOT NULL PRIMARY KEY, - CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT, - NAME VARCHAR(255) NOT NULL, - IDENTIFIER VARCHAR(255) NOT NULL, - TENANT_ID INT, - DESCRIPTION VARCHAR(255), - TYPE VARCHAR(255) NOT NULL, - REQUIRES_AUTHORIZATION BOOLEAN NOT NULL -); - -CREATE TABLE IF NOT EXISTS API_RESOURCE_PROPERTY ( - ID INTEGER AUTO_INCREMENT, - API_ID CHAR(36) NOT NULL, - NAME VARCHAR(255) NOT NULL, - `VALUE` VARCHAR(255) NOT NULL, - PRIMARY KEY (ID), - CONSTRAINT API_RESOURCE_PROPERTY_CONSTRAINT UNIQUE (API_ID, NAME), - FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS SCOPE ( - ID VARCHAR(255) NOT NULL PRIMARY KEY, - CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT, - API_ID VARCHAR(255) NOT NULL, - NAME VARCHAR(255) NOT NULL, - DISPLAY_NAME VARCHAR(255) NOT NULL, - DESCRIPTION VARCHAR(300), - TENANT_ID INT, - FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS AUTHORIZED_API( - APP_ID VARCHAR(255) NOT NULL, - API_ID VARCHAR(255) NOT NULL, - POLICY_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (APP_ID, API_ID), - FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE, - FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS AUTHORIZED_SCOPE( - APP_ID CHAR(36) NOT NULL, - API_ID CHAR(36) NOT NULL, - SCOPE_ID CHAR(36) NOT NULL, - CONSTRAINT PK_APP_API_SCOPE PRIMARY KEY (APP_ID, API_ID, SCOPE_ID), - FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID), - FOREIGN KEY (SCOPE_ID) REFERENCES SCOPE(ID) ON DELETE CASCADE, - FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID), - FOREIGN KEY (APP_ID, API_ID) REFERENCES AUTHORIZED_API(APP_ID, API_ID) ON DELETE CASCADE, - CONSTRAINT AUTHORIZED_SCOPE_UNIQUE UNIQUE (APP_ID, SCOPE_ID) -); - -CREATE TABLE IF NOT EXISTS APP_ROLE_ASSOCIATION ( - APP_ID CHAR(36) NOT NULL, - ROLE_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (APP_ID, ROLE_ID), - FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE -); - --- --------------------------- INDEX CREATION ----------------------------- --- IDN_OAUTH2_ACCESS_TOKEN -- -CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED); -CREATE INDEX IDX_ATH ON IDN_OAUTH2_ACCESS_TOKEN(ACCESS_TOKEN_HASH); -CREATE INDEX IDX_AT_TI_UD ON IDN_OAUTH2_ACCESS_TOKEN(AUTHZ_USER, TENANT_ID, TOKEN_STATE, USER_DOMAIN); -CREATE INDEX IDX_AT_AT ON IDN_OAUTH2_ACCESS_TOKEN(ACCESS_TOKEN); -CREATE INDEX IDX_AT_RTH ON IDN_OAUTH2_ACCESS_TOKEN(REFRESH_TOKEN_HASH); -CREATE INDEX IDX_AT_RT ON IDN_OAUTH2_ACCESS_TOKEN(REFRESH_TOKEN); - --- IDN_OAUTH2_AUTHORIZATION_CODE -- -CREATE INDEX IDX_AUTHORIZATION_CODE_HASH ON IDN_OAUTH2_AUTHORIZATION_CODE (AUTHORIZATION_CODE_HASH, CONSUMER_KEY_ID); -CREATE INDEX IDX_AUTHORIZATION_CODE_AU_TI ON IDN_OAUTH2_AUTHORIZATION_CODE (AUTHZ_USER, TENANT_ID, USER_DOMAIN, STATE); -CREATE INDEX IDX_AC_CKID ON IDN_OAUTH2_AUTHORIZATION_CODE(CONSUMER_KEY_ID); -CREATE INDEX IDX_AC_TID ON IDN_OAUTH2_AUTHORIZATION_CODE(TOKEN_ID); -CREATE INDEX IDX_AC_AC_CKID ON IDN_OAUTH2_AUTHORIZATION_CODE(AUTHORIZATION_CODE, CONSUMER_KEY_ID); -CREATE INDEX IDX_AT_CKID_AU_TID_UD_TSH_TS ON IDN_OAUTH2_ACCESS_TOKEN(CONSUMER_KEY_ID, AUTHZ_USER, TENANT_ID, -USER_DOMAIN, TOKEN_SCOPE_HASH, TOKEN_STATE); - --- IDN_SCIM_GROUP -- -CREATE INDEX IDX_IDN_SCIM_GROUP_TI_RN ON IDN_SCIM_GROUP (TENANT_ID, ROLE_NAME); -CREATE INDEX IDX_IDN_SCIM_GROUP_TI_RN_AN ON IDN_SCIM_GROUP (TENANT_ID, ROLE_NAME, ATTR_NAME); - --- IDN_AUTH_SESSION_STORE -- -CREATE INDEX IDX_IDN_AUTH_SESSION_TIME ON IDN_AUTH_SESSION_STORE (TIME_CREATED); - --- IDN_AUTH_TEMP_SESSION_STORE -- -CREATE INDEX IDX_IDN_AUTH_TMP_SESSION_TIME ON IDN_AUTH_TEMP_SESSION_STORE (TIME_CREATED); - --- IDN_OIDC_SCOPE_CLAIM_MAPPING -- -CREATE INDEX IDX_AT_SI_ECI ON IDN_OIDC_SCOPE_CLAIM_MAPPING(SCOPE_ID, EXTERNAL_CLAIM_ID); - --- IDN_OAUTH2_SCOPE -- -CREATE INDEX IDX_SC_TID ON IDN_OAUTH2_SCOPE(TENANT_ID); - --- IDN_OAUTH2_SCOPE_BINDING -- -CREATE INDEX IDX_SB_SCPID ON IDN_OAUTH2_SCOPE_BINDING(SCOPE_ID); - --- IDN_OIDC_REQ_OBJECT_REFERENCE -- -CREATE INDEX IDX_OROR_TID ON IDN_OIDC_REQ_OBJECT_REFERENCE(TOKEN_ID); - --- IDN_OAUTH2_ACCESS_TOKEN_SCOPE -- -CREATE INDEX IDX_ATS_TID ON IDN_OAUTH2_ACCESS_TOKEN_SCOPE(TOKEN_ID); - --- SP_TEMPLATE -- -CREATE INDEX IDX_SP_TEMPLATE ON SP_TEMPLATE (TENANT_ID, NAME); - --- IDN_AUTH_USER -- -CREATE INDEX IDX_AUTH_USER_UN_TID_DN ON IDN_AUTH_USER (USER_NAME, TENANT_ID, DOMAIN_NAME); -CREATE INDEX IDX_AUTH_USER_DN_TOD ON IDN_AUTH_USER (DOMAIN_NAME, TENANT_ID); - --- IDN_AUTH_USER_SESSION_MAPPING -- -CREATE INDEX IDX_USER_ID ON IDN_AUTH_USER_SESSION_MAPPING (USER_ID); -CREATE INDEX IDX_SESSION_ID ON IDN_AUTH_USER_SESSION_MAPPING (SESSION_ID); - --- IDN_OAUTH_CONSUMER_APPS -- -CREATE INDEX IDX_OCA_UM_TID_UD_APN ON IDN_OAUTH_CONSUMER_APPS(USERNAME,TENANT_ID,USER_DOMAIN, APP_NAME); - --- IDX_SPI_APP -- -CREATE INDEX IDX_SPI_APP ON SP_INBOUND_AUTH(APP_ID); - --- IDN_OIDC_PROPERTY -- -CREATE INDEX IDX_IOP_CK ON IDN_OIDC_PROPERTY(TENANT_ID, CONSUMER_KEY); - --- IDN_FIDO2_PROPERTY -- -CREATE INDEX IDX_FIDO2_STR ON FIDO2_DEVICE_STORE(USER_NAME, TENANT_ID, DOMAIN_NAME, CREDENTIAL_ID, USER_HANDLE); - --- IDN_ASSOCIATED_ID -- -CREATE INDEX IDX_AI_DN_UN_AI ON IDN_ASSOCIATED_ID(DOMAIN_NAME, USER_NAME, ASSOCIATION_ID); - --- IDN_OAUTH2_TOKEN_BINDING -- -CREATE INDEX IDX_IDN_AUTH_BIND ON IDN_OAUTH2_TOKEN_BINDING (TOKEN_BINDING_REF); - --- IDN_FED_AUTH_SESSION_MAPPING -- -CREATE INDEX IDX_FEDERATED_AUTH_SESSION_ID ON IDN_FED_AUTH_SESSION_MAPPING (SESSION_ID); - --- IDN_REMOTE_FETCH_REVISIONS -- -CREATE INDEX IDX_REMOTE_FETCH_REVISION_CONFIG_ID ON IDN_REMOTE_FETCH_REVISIONS (CONFIG_ID); - --- IDN_CORS_ASSOCIATION -- -CREATE INDEX IDX_CORS_SP_APP_ID ON IDN_CORS_ASSOCIATION (SP_APP_ID); - --- IDN_CORS_ASSOCIATION -- -CREATE INDEX IDX_CORS_ORIGIN_ID ON IDN_CORS_ASSOCIATION (IDN_CORS_ORIGIN_ID); diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/pom.xml b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/pom.xml similarity index 88% rename from components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/pom.xml rename to components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/pom.xml index 24f5adf220fa..5a049ebe4e56 100644 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/pom.xml +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/pom.xml @@ -26,10 +26,10 @@ ../pom.xml - org.wso2.carbon.ai.service.mgt + org.wso2.carbon.identity.ai.service.mgt bundle - WSO2 Carbon - AI Service Management Bundle - This is a Carbon bundle that represents the AI Service Management Bundle. + WSO2 Identity - AI Service Management Bundle + This represents the AI Service Management Bundle. http://wso2.org @@ -41,10 +41,6 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.core - - org.wso2.orbit.org.apache.httpcomponents - httpasyncclient - org.apache.httpcomponents.wso2 httpcore @@ -53,10 +49,12 @@ com.fasterxml.jackson.core jackson-databind + provided org.mockito mockito-core + test org.testng @@ -66,6 +64,7 @@ org.slf4j slf4j-api + provided org.apache.logging.log4j @@ -77,10 +76,10 @@ wiremock test - org.ops4j.pax.logging pax-logging-api + provided @@ -115,15 +114,10 @@ org.apache.http.protocol; version="${httpcore.version.osgi.import.range}", org.apache.http.util; version="${httpcore.version.osgi.import.range}", org.apache.http.impl.client; version="${httpcomponents-httpclient.imp.pkg.version.range}", - org.apache.http.impl.nio.client; version="${httpasyncclient.version.osgi.import.range}", - org.apache.http.impl.nio.reactor; version="${httpasyncclient.version.osgi.import.range}", - org.apache.http.impl.nio.conn; version="${httpasyncclient.version.osgi.import.range}", org.apache.http.concurrent; version="${httpcore.version.osgi.import.range}", - org.apache.http.nio.reactor; version="${httpasyncclient.version.osgi.import.range}", - org.apache.http.nio.conn; version="${httpasyncclient.version.osgi.import.range}", - org.wso2.carbon.ai.service.mgt.*; version="${carbon.identity.package.export.version}" + org.wso2.carbon.identity.ai.service.mgt.*; version="${carbon.identity.package.export.version}" @@ -189,7 +183,7 @@ COMPLEXITY COVEREDRATIO - 0.77 + 0.80 diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/constants/AIConstants.java b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/constants/AIConstants.java similarity index 61% rename from components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/constants/AIConstants.java rename to components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/constants/AIConstants.java index 252fd02f35f6..2445ca29375a 100644 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/constants/AIConstants.java +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/constants/AIConstants.java @@ -16,7 +16,7 @@ * under the License. */ -package org.wso2.carbon.ai.service.mgt.constants; +package org.wso2.carbon.identity.ai.service.mgt.constants; /** * Constants for the LoginFlowAI module. @@ -26,9 +26,16 @@ public class AIConstants { public static final String AI_SERVICE_KEY_PROPERTY_NAME = "AIServices.Key"; public static final String AI_TOKEN_ENDPOINT_PROPERTY_NAME = "AIServices.TokenEndpoint"; public static final String AI_TOKEN_SERVICE_MAX_RETRIES_PROPERTY_NAME = "AIServices.TokenRequestMaxRetries"; - public static final String AI_TOKEN_SERVICE_TIMEOUT_PROPERTY_NAME = "AIServices.TokenRequestTimeout"; + public static final String AI_TOKEN_CONNECTION_TIMEOUT_PROPERTY_NAME = "AIServices.TokenConnectionTimeout"; + public static final String AI_TOKEN_CONNECTION_REQUEST_TIMEOUT_PROPERTY_NAME = "AIServices" + + ".TokenConnectionRequestTimeout"; + public static final String AI_TOKEN_SOCKET_TIMEOUT_PROPERTY_NAME = "AIServices.TokenSocketTimeout"; + public static final String HTTP_CONNECTION_POOL_SIZE_PROPERTY_NAME = "AIServices.HTTPConnectionPoolSize"; public static final String HTTP_CONNECTION_TIMEOUT_PROPERTY_NAME = "AIServices.HTTPConnectionTimeout"; + public static final String HTTP_CONNECTION_REQUEST_TIMEOUT_PROPERTY_NAME = "AIServices" + + ".HTTPConnectionRequestTimeout"; + public static final String HTTP_SOCKET_TIMEOUT_PROPERTY_NAME = "AIServices.HTTPSocketTimeout"; // Http constants. public static final String HTTP_BASIC = "Basic"; @@ -41,23 +48,33 @@ public class AIConstants { public static final String TENANT_CONTEXT_PREFIX = "/t/"; + // Default Property values. + public static final int DEFAULT_TOKEN_REQUEST_MAX_RETRIES = 3; + public static final int DEFAULT_TOKEN_CONNECTION_TIMEOUT = 3000; + public static final int DEFAULT_TOKEN_CONNECTION_REQUEST_TIMEOUT = 3000; + public static final int DEFAULT_TOKEN_SOCKET_TIMEOUT = 3000; + + public static final int DEFAULT_HTTP_CONNECTION_POOL_SIZE = 20; + public static final int DEFAULT_HTTP_CONNECTION_TIMEOUT = 3000; + public static final int DEFAULT_HTTP_CONNECTION_REQUEST_TIMEOUT = 3000; + public static final int DEFAULT_HTTP_SOCKET_TIMEOUT = 3000; + /** * Enums for error messages. */ public enum ErrorMessages { - MAXIMUM_RETRIES_EXCEEDED("AI_10000", "Maximum retries exceeded to retrieve the access token."), - UNABLE_TO_ACCESS_AI_SERVICE_WITH_RENEW_ACCESS_TOKEN("AI_10003", "Unable to access the " + + MAXIMUM_RETRIES_EXCEEDED("AI-10000", "Maximum retries exceeded to retrieve the access token."), + UNABLE_TO_ACCESS_AI_SERVICE_WITH_RENEW_ACCESS_TOKEN("AI-10003", "Unable to access the " + "AI service with the renewed access token."), - REQUEST_TIMEOUT("AI_10004", "Request to the AI service timed out."), - ERROR_RETRIEVING_ACCESS_TOKEN("AI_10007", "Error occurred while retrieving the " + + REQUEST_TIMEOUT("AI-10004", "Request to the AI service timed out."), + ERROR_RETRIEVING_ACCESS_TOKEN("AI-10007", "Error occurred while retrieving the " + "access token."), - CLIENT_ERROR_WHILE_CONNECTING_TO_AI_SERVICE("AI_10008", "Client error occurred " + + CLIENT_ERROR_WHILE_CONNECTING_TO_AI_SERVICE("AI-10008", "Client error occurred " + "for %s tenant while connecting to AI service."), - SERVER_ERROR_WHILE_CONNECTING_TO_AI_SERVICE("AI_10009", "Server error occurred " + + SERVER_ERROR_WHILE_CONNECTING_TO_AI_SERVICE("AI-10009", "Server error occurred " + "for %s tenant while connecting to AI service."); - private final String code; private final String message; diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/exceptions/AIClientException.java b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/exceptions/AIClientException.java similarity index 70% rename from components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/exceptions/AIClientException.java rename to components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/exceptions/AIClientException.java index 3272e573177e..fa91436a07ae 100644 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/exceptions/AIClientException.java +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/exceptions/AIClientException.java @@ -16,9 +16,7 @@ * under the License. */ -package org.wso2.carbon.ai.service.mgt.exceptions; - -import org.wso2.carbon.ai.service.mgt.util.AIHttpClientUtil; +package org.wso2.carbon.identity.ai.service.mgt.exceptions; /** * Client Exception class for AI service. @@ -26,7 +24,8 @@ public class AIClientException extends Exception { private final String errorCode; - private AIHttpClientUtil.HttpResponseWrapper loginFlowAIResponse; + private String serverMessage; + private int serverStatusCode; public AIClientException(String message, String errorCode) { @@ -34,12 +33,12 @@ public AIClientException(String message, String errorCode) { this.errorCode = errorCode; } - public AIClientException(AIHttpClientUtil.HttpResponseWrapper httpResponseWrapper, - String message, String errorCode) { + public AIClientException(String message, String errorCode, int serverStatusCode, String serverMessage) { super(message); this.errorCode = errorCode; - this.loginFlowAIResponse = httpResponseWrapper; + this.serverStatusCode = serverStatusCode; + this.serverMessage = serverMessage; } public AIClientException(String message, String errorCode, Throwable cause) { @@ -53,8 +52,13 @@ public String getErrorCode() { return errorCode; } - public AIHttpClientUtil.HttpResponseWrapper getLoginFlowAIResponse() { + public String getServerMessage() { + + return serverMessage; + } + + public int getServerStatusCode() { - return loginFlowAIResponse; + return serverStatusCode; } } diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/exceptions/AIServerException.java b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/exceptions/AIServerException.java similarity index 72% rename from components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/exceptions/AIServerException.java rename to components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/exceptions/AIServerException.java index 1bdadca5a81e..39d7f25c30a9 100644 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/main/java/org/wso2/carbon/ai/service/mgt/exceptions/AIServerException.java +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/exceptions/AIServerException.java @@ -16,9 +16,7 @@ * under the License. */ -package org.wso2.carbon.ai.service.mgt.exceptions; - -import org.wso2.carbon.ai.service.mgt.util.AIHttpClientUtil; +package org.wso2.carbon.identity.ai.service.mgt.exceptions; /** * Client Exception class for AI service. @@ -26,7 +24,8 @@ public class AIServerException extends Exception { private String errorCode; - private AIHttpClientUtil.HttpResponseWrapper loginFlowAIResponse; + private String serverMessage; + private int serverStatusCode; public AIServerException(String message, String errorCode) { @@ -34,12 +33,12 @@ public AIServerException(String message, String errorCode) { this.errorCode = errorCode; } - public AIServerException(AIHttpClientUtil.HttpResponseWrapper httpResponseWrapper, - String message, String errorCode) { + public AIServerException(String message, String errorCode, int serverStatusCode, String serverMessage) { super(message); this.errorCode = errorCode; - this.loginFlowAIResponse = httpResponseWrapper; + this.serverStatusCode = serverStatusCode; + this.serverMessage = serverMessage; } public AIServerException(String message, Throwable cause) { @@ -58,8 +57,13 @@ public String getErrorCode() { return errorCode; } - public AIHttpClientUtil.HttpResponseWrapper getBrandingAIResponse() { + public String getServerMessage() { + + return serverMessage; + } + + public int getServerStatusCode() { - return loginFlowAIResponse; + return serverStatusCode; } } diff --git a/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/token/AIAccessTokenManager.java b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/token/AIAccessTokenManager.java new file mode 100644 index 000000000000..43f5e89e33c6 --- /dev/null +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/token/AIAccessTokenManager.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. 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.wso2.carbon.identity.ai.service.mgt.token; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.message.BasicHeader; +import org.apache.http.util.EntityUtils; +import org.wso2.carbon.identity.ai.service.mgt.exceptions.AIServerException; +import org.wso2.carbon.identity.core.util.IdentityUtil; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; +import java.util.UUID; + +import static org.apache.axis2.transport.http.HTTPConstants.HEADER_CONTENT_TYPE; +import static org.apache.http.HttpHeaders.AUTHORIZATION; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.ACCESS_TOKEN_KEY; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.AI_SERVICE_KEY_PROPERTY_NAME; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.AI_TOKEN_CONNECTION_REQUEST_TIMEOUT_PROPERTY_NAME; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.AI_TOKEN_CONNECTION_TIMEOUT_PROPERTY_NAME; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.AI_TOKEN_ENDPOINT_PROPERTY_NAME; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.AI_TOKEN_SERVICE_MAX_RETRIES_PROPERTY_NAME; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.AI_TOKEN_SOCKET_TIMEOUT_PROPERTY_NAME; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.CONTENT_TYPE_FORM_URLENCODED; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.DEFAULT_TOKEN_CONNECTION_REQUEST_TIMEOUT; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.DEFAULT_TOKEN_CONNECTION_TIMEOUT; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.DEFAULT_TOKEN_REQUEST_MAX_RETRIES; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.DEFAULT_TOKEN_SOCKET_TIMEOUT; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.ErrorMessages.MAXIMUM_RETRIES_EXCEEDED; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.HTTP_BASIC; +import static org.wso2.carbon.identity.core.util.IdentityTenantUtil.getTenantDomainFromContext; + +/** + * The purpose of this class is to retrieve an active token to access the AI service. + */ +public class AIAccessTokenManager { + + private static volatile AIAccessTokenManager instance; // Volatile for thread safety. + private static final Object lock = new Object(); // Lock for synchronization. + + private static final Log LOG = LogFactory.getLog(AIAccessTokenManager.class); + + private static final String AI_KEY = IdentityUtil.getProperty(AI_SERVICE_KEY_PROPERTY_NAME); + private static final String AI_TOKEN_ENDPOINT = IdentityUtil.getProperty(AI_TOKEN_ENDPOINT_PROPERTY_NAME); + + private final AccessTokenRequestHelper accessTokenRequestHelper; + + private String accessToken; + private final String clientId; + + private AIAccessTokenManager() { + + byte[] decodedBytes = Base64.getDecoder().decode(AI_KEY); + String decodedString = new String(decodedBytes, StandardCharsets.UTF_8); + String[] parts = decodedString.split(":"); + if (parts.length == 2) { + this.clientId = parts[0]; // Extract clientId. + } else { + throw new IllegalArgumentException("Invalid AI service key."); + } + this.accessTokenRequestHelper = new AccessTokenRequestHelper(AI_KEY, AI_TOKEN_ENDPOINT); + } + + /** + * Get the singleton instance of the AIAccessTokenManager. + * + * @return The singleton instance. + */ + public static AIAccessTokenManager getInstance() { + + if (instance == null) { + synchronized (lock) { + if (instance == null) { + instance = new AIAccessTokenManager(); + } + } + } + return instance; + } + + /** + * Get the access token. + * + * @param renewAccessToken Whether to renew the access token. + * @return The access token. + * @throws AIServerException If an error occurs while obtaining the access token. + */ + public String getAccessToken(boolean renewAccessToken) throws AIServerException { + + if (StringUtils.isEmpty(accessToken) || renewAccessToken) { + synchronized (AIAccessTokenManager.class) { + if (StringUtils.isEmpty(accessToken) || renewAccessToken) { + this.accessToken = accessTokenRequestHelper.requestAccessToken(); + } + } + } + return this.accessToken; + } + + /** + * Get the client ID. + * + * @return The client ID. + */ + public String getClientId() { + + return this.clientId; + } + + /** + * Helper class to request access token from the AI services. + */ + private static class AccessTokenRequestHelper { + + private final CloseableHttpClient client; + private final Gson gson; + private final String key; + private final HttpPost tokenRequest; + private static final int MAX_RETRIES = readIntProperty(AI_TOKEN_SERVICE_MAX_RETRIES_PROPERTY_NAME, + DEFAULT_TOKEN_REQUEST_MAX_RETRIES); + private static final int CONNECTION_TIMEOUT = readIntProperty(AI_TOKEN_CONNECTION_TIMEOUT_PROPERTY_NAME, + DEFAULT_TOKEN_CONNECTION_TIMEOUT); + private static final int CONNECTION_REQUEST_TIMEOUT = readIntProperty( + AI_TOKEN_CONNECTION_REQUEST_TIMEOUT_PROPERTY_NAME, DEFAULT_TOKEN_CONNECTION_REQUEST_TIMEOUT); + private static final int SOCKET_TIMEOUT = readIntProperty(AI_TOKEN_SOCKET_TIMEOUT_PROPERTY_NAME, + DEFAULT_TOKEN_SOCKET_TIMEOUT); + + AccessTokenRequestHelper(String key, String tokenEndpoint) { + + RequestConfig requestConfig = RequestConfig.custom() + .setConnectTimeout(CONNECTION_TIMEOUT) + .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT) + .setSocketTimeout(SOCKET_TIMEOUT) + .build(); + this.client = HttpClientBuilder.create() + .setDefaultRequestConfig(requestConfig).build(); + this.gson = new GsonBuilder().create(); + this.key = key; + this.tokenRequest = new HttpPost(tokenEndpoint); + } + + /** + * Request access token to access the AI services. + * + * @return the JWT access token. + * @throws AIServerException If an error occurs while requesting the access token. + */ + public String requestAccessToken() throws AIServerException { + + String tenantDomain = getTenantDomainFromContext(); + LOG.info("Initiating access token request for AI services from tenant: " + tenantDomain); + for (int attempt = 0; attempt < MAX_RETRIES; attempt++) { + try { + tokenRequest.setHeader(AUTHORIZATION, HTTP_BASIC + " " + key); + tokenRequest.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_FORM_URLENCODED); + + StringEntity entity = new StringEntity("grant_type=client_credentials&tokenBindingId=" + + UUID.randomUUID()); + entity.setContentType(new BasicHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_FORM_URLENCODED)); + tokenRequest.setEntity(entity); + + HttpResponse response = client.execute(tokenRequest); + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + String responseBody = EntityUtils.toString(response.getEntity()); + Map responseMap = gson.fromJson(responseBody, Map.class); + return (String) responseMap.get(ACCESS_TOKEN_KEY); + } else { + LOG.error("Token request failed with status code: " + + response.getStatusLine().getStatusCode()); + } + } catch (IOException e) { + throw new AIServerException("Error executing token request: " + e.getMessage(), e); + } finally { + tokenRequest.releaseConnection(); + } + } + throw new AIServerException("Failed to obtain access token after " + MAX_RETRIES + + " attempts.", MAXIMUM_RETRIES_EXCEEDED.getCode()); + } + + private static int readIntProperty(String key, int defaultValue) { + String value = IdentityUtil.getProperty(key); + return value != null ? Integer.parseInt(value) : defaultValue; + } + } +} diff --git a/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/util/AIHttpClientUtil.java b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/util/AIHttpClientUtil.java new file mode 100644 index 000000000000..5a540bc7cdf6 --- /dev/null +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/main/java/org/wso2/carbon/identity/ai/service/mgt/util/AIHttpClientUtil.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. 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.wso2.carbon.identity.ai.service.mgt.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.identity.ai.service.mgt.exceptions.AIClientException; +import org.wso2.carbon.identity.ai.service.mgt.exceptions.AIServerException; +import org.wso2.carbon.identity.ai.service.mgt.token.AIAccessTokenManager; +import org.wso2.carbon.identity.core.util.IdentityUtil; + +import java.io.IOException; +import java.util.Map; + +import static org.apache.axis2.transport.http.HTTPConstants.HEADER_CONTENT_TYPE; +import static org.apache.http.HttpHeaders.AUTHORIZATION; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.CONTENT_TYPE_JSON; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.DEFAULT_HTTP_CONNECTION_POOL_SIZE; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.DEFAULT_HTTP_CONNECTION_REQUEST_TIMEOUT; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.DEFAULT_HTTP_CONNECTION_TIMEOUT; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.DEFAULT_HTTP_SOCKET_TIMEOUT; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.ErrorMessages.CLIENT_ERROR_WHILE_CONNECTING_TO_AI_SERVICE; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.ErrorMessages.ERROR_RETRIEVING_ACCESS_TOKEN; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.ErrorMessages.SERVER_ERROR_WHILE_CONNECTING_TO_AI_SERVICE; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.ErrorMessages.UNABLE_TO_ACCESS_AI_SERVICE_WITH_RENEW_ACCESS_TOKEN; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.HTTP_BEARER; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.HTTP_CONNECTION_POOL_SIZE_PROPERTY_NAME; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.HTTP_CONNECTION_REQUEST_TIMEOUT_PROPERTY_NAME; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.HTTP_CONNECTION_TIMEOUT_PROPERTY_NAME; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.HTTP_SOCKET_TIMEOUT_PROPERTY_NAME; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.TENANT_CONTEXT_PREFIX; + +/** + * Utility class for AI Services to send HTTP requests. + */ +public class AIHttpClientUtil { + + private static final Log LOG = LogFactory.getLog(AIHttpClientUtil.class); + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private static final int HTTP_CONNECTION_POOL_SIZE = readIntProperty(HTTP_CONNECTION_POOL_SIZE_PROPERTY_NAME, + DEFAULT_HTTP_CONNECTION_POOL_SIZE); + private static final int HTTP_CONNECTION_TIMEOUT = readIntProperty(HTTP_CONNECTION_TIMEOUT_PROPERTY_NAME, + DEFAULT_HTTP_CONNECTION_TIMEOUT); + private static final int HTTP_CONNECTION_REQUEST_TIMEOUT = readIntProperty( + HTTP_CONNECTION_REQUEST_TIMEOUT_PROPERTY_NAME, DEFAULT_HTTP_CONNECTION_REQUEST_TIMEOUT); + private static final int HTTP_SOCKET_TIMEOUT = readIntProperty(HTTP_SOCKET_TIMEOUT_PROPERTY_NAME, + DEFAULT_HTTP_SOCKET_TIMEOUT); + + // Singleton instance of CloseableHttpClient with connection pooling. + private static final CloseableHttpClient httpClient = HttpClients.custom() + .setMaxConnTotal(HTTP_CONNECTION_POOL_SIZE) + .setDefaultRequestConfig( + org.apache.http.client.config.RequestConfig.custom() + .setSocketTimeout(HTTP_SOCKET_TIMEOUT) + .setConnectTimeout(HTTP_CONNECTION_TIMEOUT) + .setConnectionRequestTimeout(HTTP_CONNECTION_REQUEST_TIMEOUT) + .build() + ).build(); + + /** + * Execute a request to the AI service. + * + * @param path The endpoint to which the request should be sent. + * @param requestType The type of the request (GET, POST). + * @param requestBody The request body(Only for POST requests). + * @param aiServiceEndpoint The endpoint of the AI service. + * @return The response from the AI service as a map. + * @throws AIServerException If a server error occurred while accessing the AI service. + * @throws AIClientException If a client error occurred while accessing the AI service. + */ + public static Map executeRequest(String aiServiceEndpoint, String path, + Class requestType, Object requestBody) + throws AIServerException, AIClientException { + + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + + try { + String accessToken = AIAccessTokenManager.getInstance().getAccessToken(false); + String clientId = AIAccessTokenManager.getInstance().getClientId(); + + HttpUriRequest request = createRequest(aiServiceEndpoint + TENANT_CONTEXT_PREFIX + clientId + path, + requestType, accessToken, requestBody); + HttpResponseWrapper aiServiceResponse = executeRequestWithRetry(request); + + int statusCode = aiServiceResponse.getStatusCode(); + String responseBody = aiServiceResponse.getResponseBody(); + + if (statusCode >= 400) { + handleErrorResponse(statusCode, responseBody, tenantDomain); + } + return convertJsonStringToMap(responseBody); + } catch (IOException e) { + throw new AIServerException("An error occurred while connecting to the AI Service.", + SERVER_ERROR_WHILE_CONNECTING_TO_AI_SERVICE.getCode(), e); + } + } + + private static HttpUriRequest createRequest(String url, Class requestType, + String accessToken, Object requestBody) throws IOException { + + HttpUriRequest request; + if (requestType == HttpPost.class) { + HttpPost post = new HttpPost(url); + if (requestBody != null) { + post.setEntity(new StringEntity(objectMapper.writeValueAsString(requestBody))); + } + request = post; + } else if (requestType == HttpGet.class) { + request = new HttpGet(url); + } else { + throw new IllegalArgumentException("Unsupported request type: " + requestType.getName()); + } + + request.setHeader(AUTHORIZATION, HTTP_BEARER + " " + accessToken); + request.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON); + return request; + } + + private static HttpResponseWrapper executeRequestWithRetry(HttpUriRequest request) + throws IOException, AIServerException { + + HttpResponseWrapper response = executeHttpRequest(request); + + if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { + String newAccessToken = AIAccessTokenManager.getInstance().getAccessToken(true); + if (newAccessToken == null) { + throw new AIServerException("Failed to renew access token.", ERROR_RETRIEVING_ACCESS_TOKEN.getCode()); + } + request.setHeader(AUTHORIZATION, HTTP_BEARER + " " + newAccessToken); + response = executeHttpRequest(request); + } + return response; + } + + private static void handleErrorResponse(int statusCode, String responseBody, String tenantDomain) + throws AIServerException, AIClientException { + + if (statusCode == HttpStatus.SC_UNAUTHORIZED) { + throw new AIServerException("Failed to access AI service with renewed access token for " + + "the tenant domain: " + tenantDomain, + UNABLE_TO_ACCESS_AI_SERVICE_WITH_RENEW_ACCESS_TOKEN.getCode()); + } else if (statusCode >= 400 && statusCode < 500) { + throw new AIClientException("Client error occurred from tenant: " + tenantDomain + " with status code: '" + + statusCode + "' while accessing AI service.", + CLIENT_ERROR_WHILE_CONNECTING_TO_AI_SERVICE.getCode(), statusCode, responseBody); + } else if (statusCode >= 500) { + throw new AIServerException("Server error occurred from tenant: " + tenantDomain + " with status code: '" + + statusCode + "' while accessing AI service.", + SERVER_ERROR_WHILE_CONNECTING_TO_AI_SERVICE.getCode(), statusCode, responseBody); + } + } + + private static Map convertJsonStringToMap(String jsonString) throws AIServerException { + + try { + return objectMapper.readValue(jsonString, Map.class); + } catch (IOException e) { + throw new AIServerException("Error occurred while parsing the JSON response from the AI service.", e); + } + } + + protected static HttpResponseWrapper executeHttpRequest(HttpUriRequest httpRequest) + throws IOException { + + HttpResponse httpResponse = httpClient.execute(httpRequest); + int status = httpResponse.getStatusLine().getStatusCode(); + String response = EntityUtils.toString(httpResponse.getEntity()); + return new HttpResponseWrapper(status, response); + } + + private static int readIntProperty(String key, int defaultValue) { + String value = IdentityUtil.getProperty(key); + return value != null ? Integer.parseInt(value) : defaultValue; + } + + /** + * Wrapper class for HTTP response. + */ + public static class HttpResponseWrapper { + + private final int statusCode; + private final String responseBody; + + public HttpResponseWrapper(int statusCode, String responseBody) { + + this.statusCode = statusCode; + this.responseBody = responseBody; + } + + public int getStatusCode() { + + return statusCode; + } + + public String getResponseBody() { + + return responseBody; + } + } +} diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/constants/AIConstantsTest.java b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/constants/AIConstantsTest.java similarity index 73% rename from components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/constants/AIConstantsTest.java rename to components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/constants/AIConstantsTest.java index 7eaf4ce9fca0..4252283e28da 100644 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/constants/AIConstantsTest.java +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/constants/AIConstantsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -16,7 +16,7 @@ * under the License. */ -package org.wso2.carbon.ai.service.mgt.constants; +package org.wso2.carbon.identity.ai.service.mgt.constants; import org.testng.annotations.Test; @@ -30,36 +30,36 @@ public class AIConstantsTest { @Test public void testErrorMessages() { AIConstants.ErrorMessages errorMessage = AIConstants.ErrorMessages.MAXIMUM_RETRIES_EXCEEDED; - assertEquals(errorMessage.getCode(), "AI_10000"); + assertEquals(errorMessage.getCode(), "AI-10000"); assertEquals(errorMessage.getMessage(), "Maximum retries exceeded to retrieve the access token."); - assertEquals(errorMessage.toString(), "AI_10000:Maximum retries exceeded to retrieve the access token."); + assertEquals(errorMessage.toString(), "AI-10000:Maximum retries exceeded to retrieve the access token."); errorMessage = AIConstants.ErrorMessages.UNABLE_TO_ACCESS_AI_SERVICE_WITH_RENEW_ACCESS_TOKEN; - assertEquals(errorMessage.getCode(), "AI_10003"); + assertEquals(errorMessage.getCode(), "AI-10003"); assertEquals(errorMessage.getMessage(), "Unable to access the AI service with the renewed access token."); - assertEquals(errorMessage.toString(), "AI_10003:Unable to access the AI service with " + + assertEquals(errorMessage.toString(), "AI-10003:Unable to access the AI service with " + "the renewed access token."); errorMessage = AIConstants.ErrorMessages.REQUEST_TIMEOUT; - assertEquals(errorMessage.getCode(), "AI_10004"); + assertEquals(errorMessage.getCode(), "AI-10004"); assertEquals(errorMessage.getMessage(), "Request to the AI service timed out."); - assertEquals(errorMessage.toString(), "AI_10004:Request to the AI service timed out."); + assertEquals(errorMessage.toString(), "AI-10004:Request to the AI service timed out."); errorMessage = AIConstants.ErrorMessages.ERROR_RETRIEVING_ACCESS_TOKEN; - assertEquals(errorMessage.getCode(), "AI_10007"); + assertEquals(errorMessage.getCode(), "AI-10007"); assertEquals(errorMessage.getMessage(), "Error occurred while retrieving the access token."); - assertEquals(errorMessage.toString(), "AI_10007:Error occurred while retrieving the access token."); + assertEquals(errorMessage.toString(), "AI-10007:Error occurred while retrieving the access token."); errorMessage = AIConstants.ErrorMessages.CLIENT_ERROR_WHILE_CONNECTING_TO_AI_SERVICE; - assertEquals(errorMessage.getCode(), "AI_10008"); + assertEquals(errorMessage.getCode(), "AI-10008"); assertEquals(errorMessage.getMessage(), "Client error occurred for %s tenant while connecting to AI service."); - assertEquals(errorMessage.toString(), "AI_10008:Client error occurred for %s tenant while" + + assertEquals(errorMessage.toString(), "AI-10008:Client error occurred for %s tenant while" + " connecting to AI service."); errorMessage = AIConstants.ErrorMessages.SERVER_ERROR_WHILE_CONNECTING_TO_AI_SERVICE; - assertEquals(errorMessage.getCode(), "AI_10009"); + assertEquals(errorMessage.getCode(), "AI-10009"); assertEquals(errorMessage.getMessage(), "Server error occurred for %s tenant while connecting to AI service."); - assertEquals(errorMessage.toString(), "AI_10009:Server error occurred for %s tenant while " + + assertEquals(errorMessage.toString(), "AI-10009:Server error occurred for %s tenant while " + "connecting to AI service."); } } diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/exceptions/AIClientExceptionTest.java b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/exceptions/AIClientExceptionTest.java similarity index 74% rename from components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/exceptions/AIClientExceptionTest.java rename to components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/exceptions/AIClientExceptionTest.java index 1fd73f24c95b..6cd9a09546e9 100644 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/exceptions/AIClientExceptionTest.java +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/exceptions/AIClientExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -16,10 +16,9 @@ * under the License. */ -package org.wso2.carbon.ai.service.mgt.exceptions; +package org.wso2.carbon.identity.ai.service.mgt.exceptions; import org.testng.annotations.Test; -import org.wso2.carbon.ai.service.mgt.util.AIHttpClientUtil; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; @@ -35,18 +34,19 @@ public void testAIClientExceptionWithMessageAndErrorCode() { AIClientException exception = new AIClientException("Test message", "AI_10001"); assertEquals("Test message", exception.getMessage()); assertEquals("AI_10001", exception.getErrorCode()); - assertNull(exception.getLoginFlowAIResponse()); + assertNull(exception.getServerMessage()); } @Test public void testAIClientExceptionWithHttpResponseWrapperMessageAndErrorCode() { - AIHttpClientUtil.HttpResponseWrapper responseWrapper = new AIHttpClientUtil.HttpResponseWrapper( - 400, "Test response"); - AIClientException exception = new AIClientException(responseWrapper, "Test message", "AI_10002"); + int statusCode = 400; + String message = "Test message"; + AIClientException exception = new AIClientException("Test message", "AI_10002", statusCode, message); assertEquals("Test message", exception.getMessage()); assertEquals("AI_10002", exception.getErrorCode()); - assertEquals(responseWrapper, exception.getLoginFlowAIResponse()); + assertEquals(statusCode, exception.getServerStatusCode()); + assertEquals(message, exception.getServerMessage()); } @Test @@ -56,6 +56,5 @@ public void testAIClientExceptionWithMessageErrorCodeAndCause() { assertEquals("Test message", exception.getMessage()); assertEquals("AI_10003", exception.getErrorCode()); assertEquals(cause, exception.getCause()); - assertNull(exception.getLoginFlowAIResponse()); } } diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/exceptions/AIServerExceptionTest.java b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/exceptions/AIServerExceptionTest.java similarity index 69% rename from components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/exceptions/AIServerExceptionTest.java rename to components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/exceptions/AIServerExceptionTest.java index 17a4c992b509..5ce4298ad2b5 100644 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/exceptions/AIServerExceptionTest.java +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/exceptions/AIServerExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -16,13 +16,11 @@ * under the License. */ -package org.wso2.carbon.ai.service.mgt.exceptions; +package org.wso2.carbon.identity.ai.service.mgt.exceptions; import org.testng.annotations.Test; -import org.wso2.carbon.ai.service.mgt.util.AIHttpClientUtil; import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNull; /** * Test class for AIServerException. @@ -35,27 +33,29 @@ public void testAIServerExceptionWithMessageAndErrorCode() { AIServerException exception = new AIServerException("Test message", "AI_20001"); assertEquals("Test message", exception.getMessage()); assertEquals("AI_20001", exception.getErrorCode()); - assertNull(exception.getBrandingAIResponse()); + } @Test - public void testAIServerExceptionWithHttpResponseWrapperMessageAndErrorCode() { + public void testAIServerExceptionWithAIServerMessageAndErrorCode() { - AIHttpClientUtil.HttpResponseWrapper responseWrapper = new AIHttpClientUtil.HttpResponseWrapper( - 500, "Test response"); - AIServerException exception = new AIServerException(responseWrapper, "Test message", "AI_20002"); + String message = "Test message"; + String errorCode = "AI_20002"; + int statusCode = 500; + AIServerException exception = new AIServerException("Test message", errorCode, statusCode, message); assertEquals("Test message", exception.getMessage()); - assertEquals("AI_20002", exception.getErrorCode()); - assertEquals(responseWrapper, exception.getBrandingAIResponse()); + assertEquals(errorCode, exception.getErrorCode()); + assertEquals(statusCode, exception.getServerStatusCode()); + assertEquals(message, exception.getServerMessage()); } @Test public void testAIServerExceptionWithMessageAndCause() { + Throwable cause = new Throwable("Cause message"); AIServerException exception = new AIServerException("Test message", cause); assertEquals("Test message", exception.getMessage()); assertEquals(cause, exception.getCause()); - assertNull(exception.getBrandingAIResponse()); } @Test @@ -66,6 +66,5 @@ public void testAIServerExceptionWithMessageErrorCodeAndCause() { assertEquals("Test message", exception.getMessage()); assertEquals("AI_20003", exception.getErrorCode()); assertEquals(cause, exception.getCause()); - assertNull(exception.getBrandingAIResponse()); } } diff --git a/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/token/AIAccessTokenManagerTest.java b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/token/AIAccessTokenManagerTest.java new file mode 100644 index 000000000000..05aba138565a --- /dev/null +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/token/AIAccessTokenManagerTest.java @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. 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.wso2.carbon.identity.ai.service.mgt.token; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.http.Fault; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.ai.service.mgt.exceptions.AIServerException; + +import java.util.Base64; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; + +public class AIAccessTokenManagerTest { + + private WireMockServer wireMockServer; + private AIAccessTokenManager tokenManager; + + @BeforeMethod + public void setUp() throws Exception { + + // Reset the singleton instance + resetSingletonInstance(AIAccessTokenManager.class, "instance"); + } + + private void startWireMockServer() throws Exception { + + // Start WireMock server + wireMockServer = new WireMockServer(wireMockConfig().dynamicPort()); + wireMockServer.start(); + + // Set the AI_TOKEN_ENDPOINT to WireMock's base URL + setStaticField(AIAccessTokenManager.class, "AI_TOKEN_ENDPOINT", wireMockServer.baseUrl() + "/token"); + } + + private void setAiServiceKey(String key) throws Exception { + + String aiServiceKey = Base64.getEncoder().encodeToString((key).getBytes()); + setStaticField(AIAccessTokenManager.class, "AI_KEY", aiServiceKey); + } + + private void resetSingletonInstance(Class clazz, String fieldName) throws Exception { + + java.lang.reflect.Field instanceField = clazz.getDeclaredField(fieldName); + instanceField.setAccessible(true); + instanceField.set(null, null); // Reset the static field to null + } + + @AfterMethod + public void tearDown() { + + if (wireMockServer != null) { + wireMockServer.stop(); + wireMockServer = null; + } + } + + @Test + public void testGetAccessToken_Success() throws Exception { + + startWireMockServer(); + setAiServiceKey("testClientId:testClientSecret"); + tokenManager = AIAccessTokenManager.getInstance(); + + // Arrange: Mock a successful token response + String expectedAccessToken = "mockedAccessToken"; + wireMockServer.stubFor(post(urlEqualTo("/token")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("{\"access_token\":\"" + expectedAccessToken + "\"}"))); + + // Act: Call getAccessToken + String accessToken = tokenManager.getAccessToken(false); + + // Assert: Verify the access token + Assert.assertEquals(accessToken, expectedAccessToken, "Access token should match the mocked value."); + Assert.assertEquals(tokenManager.getClientId(), "testClientId", "Client ID should match the mocked value."); + } + + @Test(expectedExceptions = AIServerException.class) + public void testGetAccessToken_Unauthorized() throws Exception { + + startWireMockServer(); + setAiServiceKey("testClientId:testClientSecret"); + tokenManager = AIAccessTokenManager.getInstance(); + + // Arrange: Mock a 401 Unauthorized response + wireMockServer.stubFor(post(urlEqualTo("/token")) + .willReturn(aResponse() + .withStatus(401) + .withHeader("Content-Type", "application/json") + .withBody("{\"error\":\"Unauthorized\"}"))); + + // Act: Call getAccessToken and expect an exception + tokenManager.getAccessToken(false); + } + + @Test(expectedExceptions = AIServerException.class) + public void testGetAccessToken_ServerError() throws Exception { + + startWireMockServer(); + setAiServiceKey("testClientId:testClientSecret"); + tokenManager = AIAccessTokenManager.getInstance(); + + // Arrange: Mock a 500 Internal Server Error response + wireMockServer.stubFor(post(urlEqualTo("/token")) + .willReturn(aResponse() + .withStatus(500) + .withHeader("Content-Type", "application/json") + .withBody("{\"error\":\"Internal Server Error\"}"))); + + // Act: Call getAccessToken and expect an exception + tokenManager.getAccessToken(false); + } + + @Test + public void testGetAccessToken_Renewal() throws Exception { + + startWireMockServer(); + setAiServiceKey("testClientId:testClientSecret"); + tokenManager = AIAccessTokenManager.getInstance(); + + // Arrange: Mock a successful token response for renewal. + String newAccessToken = "newMockedAccessToken"; + wireMockServer.stubFor(post(urlEqualTo("/token")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("{\"access_token\":\"" + newAccessToken + "\"}"))); + + // Act: Call getAccessToken with renewal. + String accessToken = tokenManager.getAccessToken(true); + + // Assert: Verify the new access token. + Assert.assertEquals(accessToken, newAccessToken, "Access token should match the renewed mocked value."); + } + + @Test + public void testGetAccessToken_ExistingTokenReturnsWithoutRenewal() throws Exception { + + startWireMockServer(); + setAiServiceKey("testClientId:testClientSecret"); + tokenManager = AIAccessTokenManager.getInstance(); + + // Arrange: Mock a successful token response. + String existingAccessToken = "existingMockedAccessToken"; + wireMockServer.stubFor(post(urlEqualTo("/token")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("{\"access_token\":\"" + existingAccessToken + "\"}"))); + + // Act (1): First call to getAccessToken. This should fetch the token from the server. + String firstCallToken = tokenManager.getAccessToken(false); + Assert.assertEquals(firstCallToken, existingAccessToken, + "First call should retrieve the newly obtained token."); + + // Reset WireMock’s request history to track subsequent calls. + wireMockServer.resetRequests(); + + // Act (2): Second call with renewAccessToken = false and an existing token. + // This should NOT call the token endpoint again; it should return the cached token. + String secondCallToken = tokenManager.getAccessToken(false); + + // Assert + Assert.assertEquals(secondCallToken, existingAccessToken, + "Second call should return the same token without making a new request."); + // Verify that no new requests to the token endpoint were made after the first call. + wireMockServer.verify(0, postRequestedFor(urlEqualTo("/token"))); + } + + @Test(expectedExceptions = AIServerException.class, + expectedExceptionsMessageRegExp = "Failed to obtain access token after.*attempts.*") + public void testGetAccessToken_MaxRetriesExceeded() throws Exception { + + startWireMockServer(); + setAiServiceKey("testClientId:testClientSecret"); + tokenManager = AIAccessTokenManager.getInstance(); + + // Stub the /token endpoint to always return a non-200 status (e.g., 500). This simulates repeated failures. + wireMockServer.stubFor(post(urlEqualTo("/token")) + .willReturn(aResponse() + .withStatus(500) + .withHeader("Content-Type", "application/json") + .withBody("{\"error\":\"Internal Server Error\"}"))); + tokenManager.getAccessToken(false); + } + + @Test(expectedExceptions = AIServerException.class, + expectedExceptionsMessageRegExp = "Error executing token request:.*") + public void testGetAccessToken_IOException() throws Exception { + + // Start WireMock & set fields as usual. + startWireMockServer(); + setAiServiceKey("testClientId:testClientSecret"); + tokenManager = AIAccessTokenManager.getInstance(); + + // Configure WireMock to cause a network-level fault that should trigger an IOException. + wireMockServer.stubFor(post(urlEqualTo("/token")) + .willReturn(aResponse() + // This simulates a situation where the connection is abruptly reset. + .withFault(Fault.CONNECTION_RESET_BY_PEER))); + + // Act: When getAccessToken calls the endpoint, the client should throw an IOException, + // causing the catch block to throw an AIServerException with "Error executing token request: ...". + tokenManager.getAccessToken(false); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testInvalidAIServiceKey() throws Exception { + + setAiServiceKey("invalidKey"); + + // Act: Attempt to get the instance, which should throw an exception. + AIAccessTokenManager.getInstance(); + } + + @Test + public void testGetInstance_FirstTimeCreation() throws Exception { + + // 1) Reset the singleton to ensure it's null: + setAiServiceKey("testClientId:testClientSecret"); + setStaticField(AIAccessTokenManager.class, "AI_TOKEN_ENDPOINT", "http://localhost.com/token"); + + // 2) Call getInstance() the first time. + AIAccessTokenManager firstCallInstance = AIAccessTokenManager.getInstance(); + + // 3) Assert that the manager is created. + Assert.assertNotNull(firstCallInstance, "First call to getInstance() should create a new instance."); + } + + @Test + public void testGetInstance_SubsequentCallsReturnSameInstance() throws Exception { + + setAiServiceKey("testClientId:testClientSecret"); + setStaticField(AIAccessTokenManager.class, "AI_TOKEN_ENDPOINT", "http://localhost.com/token"); + + // 1) Reset the singleton to ensure it's null, then create it once. + AIAccessTokenManager firstCallInstance = AIAccessTokenManager.getInstance(); + + // 2) Call getInstance() again. + AIAccessTokenManager secondCallInstance = AIAccessTokenManager.getInstance(); + + // 3) Verify that the second call did NOT re-create the object. + Assert.assertNotNull(secondCallInstance, "Second call should still return an instance."); + Assert.assertEquals(secondCallInstance, firstCallInstance, + "Both calls should return the exact same singleton instance."); + } + + + private void setStaticField(Class clazz, String fieldName, String value) throws Exception { + + java.lang.reflect.Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + + java.lang.reflect.Field modifiersField = java.lang.reflect.Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~java.lang.reflect.Modifier.FINAL); + + field.set(null, value); // Set the static field + } +} diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/util/AIHttpClientUtilTest.java b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/util/AIHttpClientUtilTest.java similarity index 91% rename from components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/util/AIHttpClientUtilTest.java rename to components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/util/AIHttpClientUtilTest.java index 499c4bc72987..6f944feda381 100644 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/java/org/wso2/carbon/ai/service/mgt/util/AIHttpClientUtilTest.java +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/java/org/wso2/carbon/identity/ai/service/mgt/util/AIHttpClientUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -16,7 +16,7 @@ * under the License. */ -package org.wso2.carbon.ai.service.mgt.util; +package org.wso2.carbon.identity.ai.service.mgt.util; import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.http.Fault; @@ -29,11 +29,11 @@ import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import org.wso2.carbon.ai.service.mgt.exceptions.AIClientException; -import org.wso2.carbon.ai.service.mgt.exceptions.AIServerException; -import org.wso2.carbon.ai.service.mgt.token.AIAccessTokenManager; import org.wso2.carbon.base.CarbonBaseConstants; import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.identity.ai.service.mgt.exceptions.AIClientException; +import org.wso2.carbon.identity.ai.service.mgt.exceptions.AIServerException; +import org.wso2.carbon.identity.ai.service.mgt.token.AIAccessTokenManager; import java.nio.file.Paths; import java.util.HashMap; @@ -54,7 +54,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; -import static org.wso2.carbon.ai.service.mgt.constants.AIConstants.TENANT_CONTEXT_PREFIX; +import static org.wso2.carbon.identity.ai.service.mgt.constants.AIConstants.TENANT_CONTEXT_PREFIX; /** * Test class for AIHttpClientUtil. @@ -332,38 +332,6 @@ public void testExecuteRequest_ExecutionException() throws Exception { ); } - @Test(expectedExceptions = AIServerException.class) - public void testExecuteRequest_InterruptedException() throws Exception { - - // Arrange: Mock a server that responds but simulate thread interruption manually. - String path = "/test-endpoint"; - String fullPath = TENANT_CONTEXT_PREFIX + clientId + path; // This is the path that AIHttpClientUtil will use. - - // Simulate a valid server response to ensure interruption occurs in client code. - wireMockServer.stubFor(get(urlEqualTo(fullPath)) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody("{\"result\":\"SUCCESS\"}"))); - - // Simulate interruption in the thread executing the HTTP request. - Thread.currentThread().interrupt(); // Mark the thread as interrupted. - - try { - // Act: Execute the HTTP request. - String baseUrl = wireMockServer.baseUrl(); - AIHttpClientUtil.executeRequest( - baseUrl, - path, - HttpGet.class, - null - ); - } finally { - // Assert: Verify that the thread is still marked as interrupted. - Assert.assertTrue(Thread.currentThread().isInterrupted(), "Thread should be marked as interrupted"); - } - } - @Test(expectedExceptions = IllegalArgumentException.class) public void testExecuteRequest_UnsupportedRequestType() throws Exception { diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/log4j.properties b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/resources/log4j.properties similarity index 94% rename from components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/log4j.properties rename to components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/resources/log4j.properties index 5a83c3d02a16..b2ef4da808f5 100644 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/log4j.properties +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/resources/log4j.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). +# Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). # # WSO2 LLC. licenses this file to you under the Apache License, # Version 2.0 (the "License"); you may not use this file except diff --git a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/testng.xml b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/resources/testng.xml similarity index 61% rename from components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/testng.xml rename to components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/resources/testng.xml index 69d12f19d4d5..67ddc6f4f5bb 100644 --- a/components/ai-services-mgt/org.wso2.carbon.ai.service.mgt/src/test/resources/testng.xml +++ b/components/ai-services-mgt/org.wso2.carbon.identity.ai.service.mgt/src/test/resources/testng.xml @@ -1,5 +1,5 @@