diff --git a/src/js/how2validate/handler/validator_handler.ts b/src/js/how2validate/handler/validator_handler.ts index e649ecf..08101ca 100644 --- a/src/js/how2validate/handler/validator_handler.ts +++ b/src/js/how2validate/handler/validator_handler.ts @@ -5,7 +5,8 @@ import { validateGitHubPersonalAccessToken } from "../validators/github/github_p import { validateHFOrgApiKey } from "../validators/hugging_face/hf_org_api_key.js"; // Import the Hugging Face API Key validator import { validateHFUserAccessToken } from "../validators/hugging_face/hf_user_access_token.js"; // Import the Hugging Face API Key validator import { validateNpmAccessToken } from "../validators/npm/npm_access_token.js"; // Import the NPM access token validator -import { validatePagerDutyAPIKey } from "../validators/pagerduty/pagerduty_api_key.js"; // Import the PagerDuty validator +import { validateOpenAIApiKey } from "../validators/openai/openai_api_key.js"; // Import the NPM access token validator +import { validatePagerDutyAPIKey } from "../validators/pagerduty/pagerduty_api_key.js"; // Import the OpenAI API key validator import { validateSentryAuthToken } from "../validators/sentry/sentry_auth_token.js"; // Import the Sentry Auth Token validator import { validateSnykAuthKey } from "../validators/snyk/snyk_auth_key.js"; // Import the Snyk authentication key validator import { validateSonarcloudToken } from "../validators/sonarcloud/sonarcloud_token.js"; // Import the Sonarcloud token validator @@ -51,6 +52,7 @@ const serviceHandlers: Record = { hf_org_api_key: validateHFOrgApiKey, // HuggingFace API Key hf_user_access_token: validateHFUserAccessToken, // HuggingFace API Key npm_access_token: validateNpmAccessToken, // NPM access token validator + openai_api_key: validateOpenAIApiKey, // OpenAI API key pagerduty_api_key: validatePagerDutyAPIKey, // PagerDuty API key sentry_auth_token: validateSentryAuthToken, // Sentry Auth Token snyk_auth_key: validateSnykAuthKey, // Snyk auth key validator diff --git a/src/js/how2validate/validators/openai/openai_api_key.ts b/src/js/how2validate/validators/openai/openai_api_key.ts new file mode 100644 index 0000000..a6ebc37 --- /dev/null +++ b/src/js/how2validate/validators/openai/openai_api_key.ts @@ -0,0 +1,129 @@ +/** + * @module OpenAIApiKeyValidator + * @description + * This module provides functionality to validate an OpenAI API key by making an API call to the OpenAI service. + * It returns a validation status, processes the response from the API, and optionally sends an email report with the results. + * + * This module depends on various utilities for handling configuration, logging, and secret statuses. + * + * @requires axios - Used for making HTTP requests to the OpenAI API. + * @requires ../../utility/tool_utility.js - Utility functions for handling status responses and errors. + * @requires ../../handler/email_handler.js - Used for sending email reports on validation results. + */ + +import { ValidationResult } from "../../utility/interface/validationResult.js"; +import { + handleInactiveStatus, + handleErrors, + getUsernameFromEmail, + handleActiveStatus, + responseValidation, +} from "../../utility/tool_utility.js"; +import { EmailResponse } from "../../utility/interface/EmailResponse.js"; +import { SecretStatusMessage } from "../../utility/interface/secretStatusMessage.js"; +import { sendEmail } from "../../handler/email_handler.js"; +import axios from "axios"; + +/** + * @function validateOpenAIApiKey + * @description + * This function validates an OpenAI API key by making an API call to the OpenAI API. + * Based on the response from OpenAI, it checks the validity of the key, handles errors, and + * returns an appropriate validation result. + * + * If a `report` email is provided, the function also sends an email summarizing the validation results. + * + * @async + * @param {string} provider - The provider name (e.g., "OpenAI") for which the secret is being validated. + * @param {string} service - The name of the service being validated. + * @param {string} secret - The OpenAI API key to validate. + * @param {boolean} responseFlag - A flag to indicate whether detailed response data should be returned. + * @param {string} [report] - An optional email address to which a validation report should be sent. + * @param {boolean} [isBrowser=true] - Indicates if the function is called from a browser environment (default is false). + * + * @returns {Promise} - A promise that resolves to a `ValidationResult` object containing the validation result. + * + * @throws {Error} - If the validation process encounters an issue, it throws an error. + */ +export async function validateOpenAIApiKey( + provider: string, + service: string, + secret: string, + responseFlag: boolean, + report: string, + isBrowser: boolean = true +): Promise { + const validation_response = {} as SecretStatusMessage; + + const url = "https://api.openai.com/v1/organizations"; + const headers = { Authorization: `Bearer ${secret}` }; + + try { + const responseData = await axios.get(url, { headers }); + + if (responseData.status === 200) { + const activeResponse = handleActiveStatus( + provider, + service, + responseData, + responseFlag, + report, + isBrowser + ); + + validation_response.state = activeResponse.data?.validate.state!; + validation_response.message = activeResponse.data?.validate.message!; + validation_response.response = activeResponse.data?.validate.response!; + + return responseValidation(activeResponse, responseFlag); + } else { + const inactiveResponse = handleInactiveStatus( + provider, + service, + responseFlag, + responseData.data, + report, + isBrowser + ); + + validation_response.state = inactiveResponse.data?.validate.state!; + validation_response.message = inactiveResponse.data?.validate.message!; + validation_response.response = inactiveResponse.data?.validate.response!; + + return responseValidation(inactiveResponse, responseFlag); + } + } catch (error) { + const errResponse = handleErrors( + provider, + service, + responseFlag, + report, + error, + isBrowser + ); + + validation_response.state = errResponse.data?.validate.state!; + validation_response.message = errResponse.data?.validate.message!; + validation_response.response = errResponse.data?.validate.response!; + + return responseValidation(errResponse, responseFlag); + } finally { + if (report) { + const emailResponse: EmailResponse = { + provider: provider, + service: service, + state: validation_response.state, + message: validation_response.message, + response: validation_response.response, + }; + + sendEmail(report, getUsernameFromEmail(report), emailResponse) + .then(() => { + console.info("Validation report sent successfully"); + }) + .catch((error) => { + console.error("Error sending validation report", error); + }); + } + } +} diff --git a/src/js/tokenManager.json b/src/js/tokenManager.json index 9db2a63..e68c7bc 100644 --- a/src/js/tokenManager.json +++ b/src/js/tokenManager.json @@ -1217,7 +1217,7 @@ { "secret_type": "openai_api_key", "display_name": "Openai Api Key", - "is_enabled": false + "is_enabled": true } ], "Orbit": [ diff --git a/src/python/how2validate/handler/validator_handler.py b/src/python/how2validate/handler/validator_handler.py index e9efd87..bc18c28 100644 --- a/src/python/how2validate/handler/validator_handler.py +++ b/src/python/how2validate/handler/validator_handler.py @@ -8,6 +8,7 @@ from how2validate.validators.hugging_face.hf_org_api_key import validate_hf_org_api_key from how2validate.validators.hugging_face.hf_user_access_token import validate_hf_user_access_token from how2validate.validators.npm.npm_access_token import validate_npm_access_token +from how2validate.validators.openai.openai_api_key import validate_openai_api_key from how2validate.validators.pagerduty.pagerduty_api_key import validate_pagerduty_api_key from how2validate.validators.sentry.sentry_auth_token import validate_sentry_auth_token from how2validate.validators.snyk.snyk_auth_key import validate_snyk_auth_key @@ -16,6 +17,7 @@ from how2validate.utility.interface.validationResult import ValidationResult + # Create a dictionary that maps service names to their corresponding validator functions service_handlers = { "adafruit_io_key": validate_adafruit_io_key, @@ -25,6 +27,7 @@ "hf_org_api_key": validate_hf_org_api_key, "hf_user_access_token": validate_hf_user_access_token, "npm_access_token": validate_npm_access_token, + "openai_api_key": validate_openai_api_key, "pagerduty_api_key": validate_pagerduty_api_key, "sentry_auth_token": validate_sentry_auth_token, "snyk_auth_key": validate_snyk_auth_key, diff --git a/src/python/how2validate/validators/openai/openai_api_key.py b/src/python/how2validate/validators/openai/openai_api_key.py new file mode 100644 index 0000000..87cd8cc --- /dev/null +++ b/src/python/how2validate/validators/openai/openai_api_key.py @@ -0,0 +1,112 @@ +import json +import logging +import requests +from how2validate.handler.email_handler import send_email +from how2validate.utility.interface.EmailResponse import EmailResponse +from how2validate.utility.interface.validationResult import ValidationProcess, ValidationResult +from how2validate.utility.tool_utility import handle_active_status, handle_inactive_status, handle_errors, response_validation + +def validate_openai_api_key(provider: str, service: str, secret: str, response_flag: bool, report: str, is_browser: bool = True) -> ValidationResult: + """ + Validates the OpenAI API key by making a request to the OpenAI API. + + Parameters: + - provider (str): The provider name (e.g., "OpenAI"). + - service (str): The name of the service being validated. + - secret (str): The OpenAI API key to validate. + - response_flag (bool): Flag to indicate whether detailed response data should be returned. + - report (str): An optional email address to which a validation report should be sent. + - is_browser (bool): Indicates if the function is called from a browser environment (default is True). + + Returns: + - ValidationResult: A structured response indicating the validation results. + """ + # Initialize the response structure as an instance of the ValidationProcess class + validation_response = ValidationProcess( + state="", + message="", + response=None, + report="email@how2validate.com" + ) + + # OpenAI API endpoint for getting the organization details + url = "https://api.openai.com/v1/organizations" + + # Headers to ensure no caching and to authorize the request using the provided API key (token) + nocache_headers = {'Cache-Control': 'no-cache'} + headers_map = {'Authorization': f'Bearer {secret}'} + + try: + # Send a GET request to the OpenAI API with combined headers (nocache + authorization) + response_data = requests.get(url, headers={**nocache_headers, **headers_map}) + # Raise an HTTPError if the response has an unsuccessful status code (4xx or 5xx) + response_data.raise_for_status() + # Check if the request was successful (HTTP 200) + if response_data.status_code == 200: + # If the token is valid, handle active status + active_response = handle_active_status( + provider, + service, + response_data, + response_flag, + report, + is_browser + ) + + validation_response.state = active_response.data.validate.state + validation_response.message = active_response.data.validate.message + validation_response.response = json.dumps(active_response.to_dict(), indent=4) + + return response_validation(active_response, response_flag) + + except requests.HTTPError as error: + if 400 <= error.response.status_code < 500: + # Handle inactive token or other statuses + inactive_response = handle_inactive_status( + provider, + service, + response_flag, + error, + report, + is_browser + ) + + validation_response.state = inactive_response.data.validate.state + validation_response.message = inactive_response.data.validate.message + validation_response.response = json.dumps(inactive_response.to_dict(), indent=4) + + return response_validation(inactive_response, response_flag) + + elif 500 <= error.response.status_code < 600: + # Handle server-side errors + error_response = handle_errors( + provider, + service, + response_flag, + report, + error, + is_browser + ) + + validation_response.state = error_response.data.validate.state + validation_response.message = error_response.data.validate.message + validation_response.response = json.dumps(error_response.to_dict(), indent=4) + + return response_validation(error_response, response_flag) + + finally: + # If a report email is provided, send the validation result via email + if report: + email_response = EmailResponse( + email=report, + provider=provider, + service=service, + state=validation_response.state, + message=validation_response.message, + response=validation_response.response, + ) + try: + send_email(email_response) + logging.info('Validation report sent successfully') + except Exception as e: + logging.error('Error sending validation report', exc_info=True) diff --git a/src/python/tokenManager.json b/src/python/tokenManager.json index 9db2a63..e68c7bc 100644 --- a/src/python/tokenManager.json +++ b/src/python/tokenManager.json @@ -1217,7 +1217,7 @@ { "secret_type": "openai_api_key", "display_name": "Openai Api Key", - "is_enabled": false + "is_enabled": true } ], "Orbit": [