From 7e1e6ccbf2e2ddba7203325c1f5ccfa954044dc0 Mon Sep 17 00:00:00 2001 From: Vigneshkna Date: Wed, 20 Nov 2024 19:46:24 +0530 Subject: [PATCH] feat(Slack): Slack Api Token added --- .../how2validate/handler/validator_handler.ts | 2 + .../validators/slack/slack_api_token.ts | 147 ++++++++++++++++++ src/js/tokenManager.json | 2 +- .../how2validate/handler/validator_handler.py | 3 +- .../validators/slack/slack_api_token.py | 112 +++++++++++++ src/python/tokenManager.json | 2 +- 6 files changed, 265 insertions(+), 3 deletions(-) create mode 100644 src/js/how2validate/validators/slack/slack_api_token.ts create mode 100644 src/python/how2validate/validators/slack/slack_api_token.py diff --git a/src/js/how2validate/handler/validator_handler.ts b/src/js/how2validate/handler/validator_handler.ts index 08101ca..6773584 100644 --- a/src/js/how2validate/handler/validator_handler.ts +++ b/src/js/how2validate/handler/validator_handler.ts @@ -8,6 +8,7 @@ import { validateNpmAccessToken } from "../validators/npm/npm_access_token.js"; 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 { validateSlackApiToken } from "../validators/slack/slack_api_token.js"; // Import the Slack API 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 @@ -55,6 +56,7 @@ const serviceHandlers: Record = { openai_api_key: validateOpenAIApiKey, // OpenAI API key pagerduty_api_key: validatePagerDutyAPIKey, // PagerDuty API key sentry_auth_token: validateSentryAuthToken, // Sentry Auth Token + slack_api_token: validateSlackApiToken, // Slack API token snyk_auth_key: validateSnykAuthKey, // Snyk auth key validator sonarcloud_token: validateSonarcloudToken, // Sonarcloud token validator // Add additional services and their validators here diff --git a/src/js/how2validate/validators/slack/slack_api_token.ts b/src/js/how2validate/validators/slack/slack_api_token.ts new file mode 100644 index 0000000..6fbcf3d --- /dev/null +++ b/src/js/how2validate/validators/slack/slack_api_token.ts @@ -0,0 +1,147 @@ +/** + * @module SlackApiTokenValidator + * @description + * This module provides functionality to validate a Slack API token by making an API call to the Slack service. + * It processes the response from the API, checks if the token is valid, and optionally sends an email report with the validation results. + * + * The module depends on various utilities for handling configuration, logging, and secret statuses. + * + * @requires axios - Used for making HTTP requests to the Slack 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"; // Interface for validation results +import { + handleInactiveStatus, + handleErrors, + getUsernameFromEmail, + handleActiveStatus, + responseValidation +} from "../../utility/tool_utility.js"; // Utility functions for handling statuses and errors +import { EmailResponse } from "../../utility/interface/EmailResponse.js"; // Interface for email response structure +import { SecretStatusMessage } from "../../utility/interface/secretStatusMessage.js"; // Interface for secret status messages +import { sendEmail } from "../../handler/email_handler.js"; // Function to send email using Zoho ZeptoMail +import axios from "axios"; // Axios for making API requests + +/** + * @function validateSlackApiToken + * @description + * This function validates a Slack API token by making an API call to the Slack API. + * Based on the response from Slack, it checks the validity of the token, 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., "Slack") for which the secret is being validated. + * @param {string} service - The name of the service being validated. + * @param {string} secret - The Slack API token or secret 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. + * + * @example + * const validation = await validateSlackApiToken("Slack", "slack_api_token", "your-slack-token", true, "user@example.com"); + * console.log(validation); + */ +export async function validateSlackApiToken( + provider: string, + service: string, + secret: string, + responseFlag: boolean, + report: string, + isBrowser: boolean = true +): Promise { + // Initialize the response structure + const validationResponse = {} as SecretStatusMessage; + + // Define Slack API endpoint for checking token authentication + const url = "https://slack.com/api/auth.test?pretty=1"; // Slack API endpoint to validate the token + const nocacheHeaders = { "Cache-Control": "no-cache" }; // Avoid cache when making the request + const headers = { Authorization: `Bearer ${secret}` }; // Attach the Slack authentication token in the request headers + + try { + // Send a GET request to Slack API to validate the token + const responseData = await axios.get(url, { + headers: { ...nocacheHeaders, ...headers }, + }); + + // If the token is valid (successful response), handle active status + if (responseData.status === 200) { + const activeResponse = handleActiveStatus( + provider, + service, + responseData, + responseFlag, + report, + isBrowser + ); + + validationResponse.state = activeResponse.data?.validate.state!; + validationResponse.message = activeResponse.data?.validate.message!; + validationResponse.response = activeResponse.data?.validate.response!; + + // Return the formatted validation result + return responseValidation(activeResponse, responseFlag); + } else { + // Handle inactive token or other statuses + const inactiveResponse = handleInactiveStatus( + provider, + service, + responseFlag, + responseData.data, + report, + isBrowser + ); + + validationResponse.state = inactiveResponse.data?.validate.state!; + validationResponse.message = inactiveResponse.data?.validate.message!; + validationResponse.response = inactiveResponse.data?.validate.response!; + + // Return the formatted validation result + return responseValidation(inactiveResponse, responseFlag); + } + } catch (error) { + // Handle errors in the validation process + const errResponse = handleErrors( + provider, + service, + responseFlag, + report, + error, + isBrowser + ); + + validationResponse.state = errResponse.data?.validate.state!; + validationResponse.message = errResponse.data?.validate.message!; + validationResponse.response = errResponse.data?.validate.response!; + + // Return the error response as the validation result + return responseValidation(errResponse, responseFlag); + } finally { + // If a report email is provided, send the validation result via email + if (report) { + const emailResponse: EmailResponse = { + provider: provider, + service: service, + state: validationResponse.state, + message: validationResponse.message, + response: validationResponse.response, + }; + + // Send the email and log success or failure + 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 e68c7bc..5032622 100644 --- a/src/js/tokenManager.json +++ b/src/js/tokenManager.json @@ -1521,7 +1521,7 @@ { "secret_type": "slack_api_token", "display_name": "Slack Api Token", - "is_enabled": false + "is_enabled": true }, { "secret_type": "slack_incoming_webhook_url", diff --git a/src/python/how2validate/handler/validator_handler.py b/src/python/how2validate/handler/validator_handler.py index bc18c28..7566a49 100644 --- a/src/python/how2validate/handler/validator_handler.py +++ b/src/python/how2validate/handler/validator_handler.py @@ -11,13 +11,13 @@ 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.slack.slack_api_token import validate_slack_api_token from how2validate.validators.snyk.snyk_auth_key import validate_snyk_auth_key from how2validate.validators.sonarcloud.sonarcloud_token import validate_sonarcloud_token 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, @@ -30,6 +30,7 @@ "openai_api_key": validate_openai_api_key, "pagerduty_api_key": validate_pagerduty_api_key, "sentry_auth_token": validate_sentry_auth_token, + "slack_api_token": validate_slack_api_token, "snyk_auth_key": validate_snyk_auth_key, "sonarcloud_token": validate_sonarcloud_token, # Add additional service validators as needed diff --git a/src/python/how2validate/validators/slack/slack_api_token.py b/src/python/how2validate/validators/slack/slack_api_token.py new file mode 100644 index 0000000..b133db7 --- /dev/null +++ b/src/python/how2validate/validators/slack/slack_api_token.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_slack_api_token(provider: str, service: str, secret: str, response_flag: bool, report: str, is_browser: bool = True) -> ValidationResult: + """ + Validates the Slack API token by making a request to the Slack API. + + Parameters: + - provider (str): The provider name (e.g., "Slack"). + - service (str): The name of the service being validated. + - secret (str): The Slack API token 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=report + ) + + # Slack API endpoint for testing authentication + url = "https://slack.com/api/auth.test?pretty=1" + + # Headers for authorization using the provided Slack API token + headers = {'Authorization': f'Bearer {secret}'} + + try: + # Send a GET request to the Slack API with the authorization header + response_data = requests.get(url, headers=headers) + response_data.raise_for_status() # Raise an exception for HTTP error responses + + # Check if the request was successful (HTTP 200) + if response_data.status_code == 200: + # Handle active token 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 client errors + 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 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 e68c7bc..5032622 100644 --- a/src/python/tokenManager.json +++ b/src/python/tokenManager.json @@ -1521,7 +1521,7 @@ { "secret_type": "slack_api_token", "display_name": "Slack Api Token", - "is_enabled": false + "is_enabled": true }, { "secret_type": "slack_incoming_webhook_url",