Skip to content

Commit

Permalink
Horizon 2.5 (#14)
Browse files Browse the repository at this point in the history
* Add webra import workflow

* improve error logs

* add revoke by certificate id

* prepare release

* add tests for horizon_import

* fix tests

* update import tests

* catch ssl error

* improve bug fix of content-type in case of bad authentication

* Improve integration tests

* fix integration tests
  • Loading branch information
AdrienDucourthial authored Aug 21, 2024
1 parent 2cf981b commit 8115988
Show file tree
Hide file tree
Showing 16 changed files with 690 additions and 11 deletions.
1 change: 1 addition & 0 deletions .github/workflows/run_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ jobs:
ansible-core-version: stable-2.14
target-python-version: 3.9
testing-type: integration
test-deps: community.crypto
pre-test-cmd: >-
echo "[endpoint: ${{ secrets.HORIZON_ENDPOINT }}, x_api_id: ${{ secrets.HORIZON_API_ID }}, x_api_key: ${{ secrets.HORIZON_API_KEY }}]" >> tests/integration/integration_config.yml
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ This collection requires Python 3.6 or greater. It offers compatibility with the

| Collection version | Horizon version |
|--------------------|-----------------|
| 1.3.0 | 2.2.0+ |
| 1.4.0 | 2.2.0+ |
| 1.3.0 | 2.2.0 - 2.4.x |
| 1.2.0 | 2.2.0 - 2.3.x |
| 1.1.0 | 2.2.0 - 2.3.x |
| 1.0.1 | 2.0.0 - 2.3.x |
Expand Down
2 changes: 1 addition & 1 deletion galaxy.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace: evertrust
name: horizon
version: 1.3.0
version: 1.4.0
readme: README.md
authors:
- EverTrust R&D (@Evertrust)
Expand Down
42 changes: 42 additions & 0 deletions plugins/action/horizon_import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Standard base includes and define this as a metaclass of type
from __future__ import (absolute_import, division, print_function)

__metaclass__ = type

from ansible.errors import AnsibleError
from ansible_collections.evertrust.horizon.plugins.module_utils.horizon_action import HorizonAction
from ansible_collections.evertrust.horizon.plugins.module_utils.horizon_crypto import HorizonCrypto
from ansible_collections.evertrust.horizon.plugins.module_utils.horizon_errors import HorizonError


class ActionModule(HorizonAction):
TRANSFERS_FILES = True

def _args(self):
return ['profile', 'certificate_id', 'certificate_pem', 'private_key', 'labels', 'metadata', 'owner', 'team', 'contact_email']

def run(self, tmp=None, task_vars=None):
result = super(ActionModule, self).run(tmp, task_vars)

try:
client = self._get_client()
content = self._get_content()
response = client.webra_import(**content)

if "certificate" in response:
result["certificate"] = response["certificate"]
result["chain"] = client.chain(result["certificate"]["certificate"])

if "pkcs12" in response.keys():
result["p12"] = response["pkcs12"]["value"]
result["p12_password"] = response["password"]["value"]
result["key"] = HorizonCrypto.get_key_from_p12(response["pkcs12"]["value"],
response["password"]["value"])

except HorizonError as e:
raise AnsibleError(e.full_message)

return result
2 changes: 1 addition & 1 deletion plugins/action/horizon_revoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ActionModule(HorizonAction):
TRANSFERS_FILES = True

def _args(self):
return ["certificate_pem", "revocation_reason", "skip_already_revoked"]
return ["certificate_pem", "certificate_id", "revocation_reason", "skip_already_revoked"]

def run(self, tmp=None, task_vars=None):
result = super(ActionModule, self).run(tmp, task_vars)
Expand Down
44 changes: 40 additions & 4 deletions plugins/module_utils/horizon.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def renew(self, certificate_pem, certificate_id, password=None):

return self.post(self.REQUEST_SUBMIT_URL, json)

def revoke(self, certificate_pem, revocation_reason):
def revoke(self, certificate_pem, certificate_id, revocation_reason):
"""
Revoke a certificate
:type certificate_pem: Union[str,dict]
Expand All @@ -187,6 +187,7 @@ def revoke(self, certificate_pem, revocation_reason):
json = {
"workflow": "revoke",
"certificatePem": self.__load_file_or_string(certificate_pem),
"certificateId": certificate_id,
"revocationReason": revocation_reason,
"template": {
"revocationReason": revocation_reason
Expand Down Expand Up @@ -230,6 +231,36 @@ def update(self, certificate_pem, labels=None, metadata=None, owner=None, team=N

return self.post(self.REQUEST_SUBMIT_URL, json)

def webra_import(self, profile, certificate_pem, certificate_id, private_key, labels=None, metadata=None, owner=None, team=None, contact_email=None):

if metadata is None:
metadata = {}
if labels is None:
labels = {}

json = {
"workflow": "import",
"profile": profile,
"template": {
"privateKey": self.__load_file_or_string(private_key),
"metadata": self.__set_metadata(metadata),
"labels": self.__set_labels(labels)
},
"certificateId": certificate_id,
"certificatePem": self.__load_file_or_string(certificate_pem)
}

if owner is not None:
json["template"]["owner"] = {"value": owner}
if team is not None:
json["template"]["team"] = {"value": team}
if "contact_email" in metadata:
json["template"]["contactEmail"] = {"value": metadata["contact_email"]}
elif contact_email is not None:
json["template"]["contactEmail"] = {"value": contact_email}

return self.post(self.REQUEST_SUBMIT_URL, json)

def search(self, query=None, fields=None):
"""
Search for certificates
Expand Down Expand Up @@ -357,6 +388,8 @@ def __check_password_policy(password, template):
password_mode = template["template"]["passwordMode"]
if "passwordPolicy" in template["template"]:
password_policy = template["template"]["passwordPolicy"]
else:
password_policy = -1
# Check if the password is needed and given
if password_mode == "manual" and password is None:
message = f'A password is required. '
Expand Down Expand Up @@ -441,9 +474,12 @@ def send(self, method, path, **kwargs):
"""
uri = self.endpoint + path
method = method.upper()
response = requests.request(method, uri, cert=self.cert, verify=self.bundle,
headers=self.headers, **kwargs)
if 'Content-Type' in response.headers and response.headers['Content-Type'] == 'application/json':
try:
response = requests.request(method, uri, cert=self.cert, verify=self.bundle, headers=self.headers, **kwargs)
except requests.exceptions.SSLError:
raise AnsibleError("Got an SSL error try using the 'ca_bundle' paramater")

if 'Content-Type' in response.headers and response.headers['Content-Type'] in ['application/json', 'application/problem+json']:
content = response.json()
else:
content = response.content.decode()
Expand Down
3 changes: 3 additions & 0 deletions plugins/module_utils/horizon_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ def __init__(self, code, message, response, detail=None):
self.full_message = "Error %s : %s" % (self.code, self.message)
if self.detail:
self.full_message = "%s (%s)" % (self.full_message, self.detail)

def __str__(self):
return self.full_message
Loading

0 comments on commit 8115988

Please sign in to comment.