Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version 2.0.18 #187

Merged
merged 10 commits into from
May 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/github-actions-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ jobs:
name: appinspect-report-json
path: output/report_appinspect.json

- name: Archive tgz application
if: always()
uses: actions/upload-artifact@v3
with:
path: output/*.tgz

- name: Archive sha256sum
if: always()
uses: actions/upload-artifact@v3
with:
name: release-sha256.txt
path: output/release-sha256.txt

- name: Show output directory content
run: |
ls -ltr output/
Expand Down
6 changes: 3 additions & 3 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ sphinx:
# - pdf

# Optionally declare the Python requirements required to build your docs
#python:
# install:
# - requirements: docs/requirements.txt
python:
install:
- requirements: docs/requirements.txt
403 changes: 311 additions & 92 deletions build/build.py

Large diffs are not rendered by default.

147 changes: 98 additions & 49 deletions build/libs/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
import requests
from requests.auth import HTTPBasicAuth


# context manager
class cd:
"""Context manager for changing the current working directory"""

def __init__(self, newPath):
self.newPath = os.path.expanduser(newPath)

Expand All @@ -31,35 +33,47 @@ def __exit__(self, etype, value, traceback):

# login to Appinspect API and return the token
def login_appinspect(username, password):

login_url = "https://api.splunk.com/2.0/rest/login/splunk"

try:
response = requests.get(login_url, auth = HTTPBasicAuth(username, password), verify=True)
response = requests.get(
login_url, auth=HTTPBasicAuth(username, password), verify=True
)
if response.status_code not in (200, 201, 204):
logging.error("Authentication to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(login_url, response.status_code, response.text))
logging.error(
"Authentication to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(
login_url, response.status_code, response.text
)
)
else:
logging.debug("Authentication to Splunk Appinspect API was successful, url=\"{}\", token=\"{}\"".format(login_url, response.text))
logging.debug(
'Authentication to Splunk Appinspect API was successful, url="{}", token="{}"'.format(
login_url, response.text
)
)

except Exception as e:
logging.error("Authentication to Splunk Appinspect API has failed, url=\"{}\", exception=\"{}\"".format(login_url, e))
logging.error(
'Authentication to Splunk Appinspect API has failed, url="{}", exception="{}"'.format(
login_url, e
)
)

response_json = json.loads(response.text)
appinspect_token = response_json['data']['token']
appinspect_token = response_json["data"]["token"]

return appinspect_token


# submit an app to Appinspect
def submit_appinspect(session, token, app):

appinspect_headers = {
'Authorization': 'bearer %s' % token,
}
"Authorization": "bearer %s" % token,
}

files = {
'app_package': open(app, 'rb'),
'included_tags': (None, 'cloud'),
"app_package": open(app, "rb"),
"included_tags": (None, "cloud"),
}

# submit
Expand All @@ -69,55 +83,67 @@ def submit_appinspect(session, token, app):
session = requests.Session()

try:
response = session.post(validate_url, headers=appinspect_headers, files=files, verify=True)
response = session.post(
validate_url, headers=appinspect_headers, files=files, verify=True
)
if response.status_code not in (200, 201, 204):
logging.error("Submission to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(validate_url, response.status_code, response.text))
logging.error(
"Submission to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(
validate_url, response.status_code, response.text
)
)
else:
logging.debug("Submission to Splunk Appinspect API was successful, url=\"{}\", response=\"{}\"".format(validate_url, response.text))
logging.debug(
'Submission to Splunk Appinspect API was successful, url="{}", response="{}"'.format(
validate_url, response.text
)
)

except Exception as e:
logging.error("Submission to Splunk Appinspect API has failed, url=\"{}\", exception=\"{}\"".format(validate_url, e))
logging.error(
'Submission to Splunk Appinspect API has failed, url="{}", exception="{}"'.format(
validate_url, e
)
)

return response.text


# verify an Appinspect vetting status
def verify_appinspect(session, token, request_id, max_retries=10, wait_time=5):

def verify_appinspect(session, token, request_id):
appinspect_headers = {
'Authorization': 'bearer %s' % token,
}
"Authorization": "bearer %s" % token,
}

# submit
validate_url = "https://appinspect.splunk.com/v1/app/validate/status/" + str(request_id)
validate_url = f"https://appinspect.splunk.com/v1/app/validate/status/{request_id}"

# run
for i in range(max_retries):
try:
response = session.get(validate_url, headers=appinspect_headers, verify=True)
if response.status_code not in (200, 201, 204):
logging.error("Request verification to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(validate_url, response.status_code, response.text))
else:
logging.debug("Request verification to Splunk Appinspect API was successful, url=\"{}\", response=\"{}\"".format(validate_url, response.text))
return response.text
try:
response = session.get(validate_url, headers=appinspect_headers, verify=True)
if response.status_code not in (200, 201, 204):
error_message = f"Request verification to Splunk Appinspect API has failed, url={validate_url}, HTTP Error={response.status_code}, content={response.text}"
logging.error(error_message)
raise Exception(error_message)

except Exception as e:
if i < max_retries - 1: # Not the last attempt
logging.warning("Request verification to Splunk Appinspect API has failed, url=\"{}\", exception=\"{}\". Retrying...".format(validate_url, e))
time.sleep(wait_time)
else:
logging.error("Request verification to Splunk Appinspect API has failed, url=\"{}\", exception=\"{}\"".format(validate_url, e))
else:
logging.debug(
'Request verification to Splunk Appinspect API was successful, url="{}", response="{}"'.format(
validate_url, response.text
)
)
return response.text

return None
except Exception as e:
raise Exception(str(e))


# download an Appinspect vetting report
def download_htmlreport_appinspect(session, token, request_id):

appinspect_headers = {
'Authorization': 'bearer %s' % token,
'Content-Type': 'text/html',
}
"Authorization": "bearer %s" % token,
"Content-Type": "text/html",
}

# submit
validate_url = "https://appinspect.splunk.com/v1/app/report/" + str(request_id)
Expand All @@ -126,24 +152,35 @@ def download_htmlreport_appinspect(session, token, request_id):
try:
response = session.get(validate_url, headers=appinspect_headers, verify=True)
if response.status_code not in (200, 201, 204):
logging.error("Report download request to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(validate_url, response.status_code, response.text))
logging.error(
"Report download request to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(
validate_url, response.status_code, response.text
)
)
return None
else:
logging.debug("Report download request to Splunk Appinspect API was successful, url=\"{}\", response=\"{}\"".format(validate_url, response.text))
logging.debug(
'Report download request to Splunk Appinspect API was successful, url="{}", response="{}"'.format(
validate_url, response.text
)
)
return response.text

except Exception as e:
logging.error("Report download request to Splunk Appinspect API has failed, url=\"{}\", exception=\"{}\"".format(validate_url, e))
logging.error(
'Report download request to Splunk Appinspect API has failed, url="{}", exception="{}"'.format(
validate_url, e
)
)
return None


# download an Appinspect vetting report
def download_jsonreport_appinspect(session, token, request_id):

appinspect_headers = {
'Authorization': 'bearer %s' % token,
'Content-Type': 'application/json',
}
"Authorization": "bearer %s" % token,
"Content-Type": "application/json",
}

# submit
validate_url = "https://appinspect.splunk.com/v1/app/report/" + str(request_id)
Expand All @@ -152,12 +189,24 @@ def download_jsonreport_appinspect(session, token, request_id):
try:
response = session.get(validate_url, headers=appinspect_headers, verify=True)
if response.status_code not in (200, 201, 204):
logging.error("Report download request to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(validate_url, response.status_code, response.text))
logging.error(
"Report download request to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(
validate_url, response.status_code, response.text
)
)
return None
else:
logging.debug("Report download request to Splunk Appinspect API was successful, url=\"{}\", response=\"{}\"".format(validate_url, response.text))
logging.debug(
'Report download request to Splunk Appinspect API was successful, url="{}", response="{}"'.format(
validate_url, response.text
)
)
return response.text

except Exception as e:
logging.error("Report download request to Splunk Appinspect API has failed, url=\"{}\", exception=\"{}\"".format(validate_url, e))
logging.error(
'Report download request to Splunk Appinspect API has failed, url="{}", exception="{}"'.format(
validate_url, e
)
)
return None
2 changes: 2 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ About SSL certificate validation

SSL usage is enforced, this means you cannot access to a JIRA instance if it is not using SSL.

**Since the version 2.0.18, SSL certificate verification is mandatory due to Splunk Cloud vetting requirements.**

**When using SSL, there might be different conditions:**

- The SSL certificate is from an official third party certificate authority and it is valid, you normally can tick the SSL validation box which enforces the fact that we expect the certificate to be valid. If the certificate is not valid, we will not allow any action to be performed.
Expand Down
16 changes: 16 additions & 0 deletions docs/releasenotes.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
Release notes
#############

Version 2.0.18
==============

- Fix Issue#184 - Fix - Splunk Cloud vetting - Force SSL Verification or the use of a CA bundle to validate the SSL verification #184
- Fix Issue#185 - Build - Prevents pyc, hidden files or directory to be part of the release #185
- Fix Issue#186 - Pypi requirements libs refresh #186
- Fix Issue#188 - Number of projects count in Overview - JIRA Projects is wrong #188
- Fix Issue#189 - Avoid app nav bar ending in multiple lines #189

Version 2.0.17
==============

- enhancement - Custom fields - Add additional protection against badly parsed custom fields #174
- enhancement - Allows adding the Splunk search results in the JIRA Description field (in CSV or JSON format) #173
- bug - Splunk Cloud Classic - ensures that indexed time parsing is partioned on the Search Heads properly (SLIM) #176

Version 2.0.16
==============

Expand Down
3 changes: 3 additions & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
sphinx>=7.2.6
sphinx-rtd-theme>=2.0.0
jinja2>=3.1.4
28 changes: 4 additions & 24 deletions globalConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,31 +118,10 @@
"link": "https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html"
}
},
{
"type": "radio",
"label": "SSL certificate validation",
"field": "jira_ssl_certificate_validation",
"defaultValue": "1",
"help": "Verify that the SSL certificate is a valid certificate provided by a trusted entity",
"required": true,
"options": {
"items": [
{
"value": "1",
"label": "Yes"
},
{
"value": "0",
"label": "No"
}
],
"display": true
}
},
{
"type": "text",
"label": "SSL certificate path",
"help": "To verify a self-signed or internal PKI certificate, you can specifify the local path to the PEM file",
"help": "SSL certificate verication is mandatory, to verify a self-signed or internal PKI certificate, you must specify the local path to the certificate PEM file or bundle",
"validators": [
{
"type": "string",
Expand Down Expand Up @@ -668,8 +647,9 @@
"meta": {
"name": "TA-jira-service-desk-simple-addon",
"restRoot": "ta_service_desk_simple_addon",
"version": "2.0.17",
"version": "2.0.18",
"displayName": "JIRA Service Desk simple addon",
"schemaVersion": "0.0.3"
"schemaVersion": "0.0.3",
"_uccVersion": "5.39.1"
}
}
Loading
Loading