barbican/barbican/plugin/symantec.py

292 lines
11 KiB
Python

# Copyright (c) 2013-2014 Rackspace, Inc.
#
# Licensed 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.
"""
Barbican certificate processing plugins and support.
"""
from oslo_config import cfg
from requests import exceptions as request_exceptions
from symantecssl.core import Symantec
from symantecssl import exceptions as symantec_exceptions
from barbican.common import config
from barbican import i18n as u
from barbican.plugin.interface import certificate_manager as cert
CONF = config.new_config()
symantec_plugin_group = cfg.OptGroup(name='symantec_plugin',
title='Symantec Plugin Options')
symantec_plugin_opts = [
cfg.StrOpt('username',
help=u._('Symantec username for authentication')),
cfg.StrOpt('password',
help=u._('Symantec password for authentication')),
cfg.StrOpt('url',
help=u._('Domain of Symantec API'))
]
CONF.register_group(symantec_plugin_group)
CONF.register_opts(symantec_plugin_opts, group=symantec_plugin_group)
config.parse_args(CONF)
class SymantecCertificatePlugin(cert.CertificatePluginBase):
"""Symantec certificate plugin."""
def __init__(self, conf=CONF):
self.username = conf.symantec_plugin.username
self.password = conf.symantec_plugin.password
self.url = conf.symantec_plugin.url
if self.username == None:
raise ValueError(u._("username is required"))
if self.password == None:
raise ValueError(u._("password is required"))
if self.url == None:
raise ValueError(u._("url is required"))
def get_default_ca_name(self):
return "Symantec CA"
def get_default_signing_cert(self):
# TODO(chellygel) Add code to get the signing cert
return None
def get_default_intermediates(self):
# TODO(chellygel) Add code to get the cert chain
return None
def issue_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Create the initial order with CA
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf.
:param barbican_meta_dto: additional data needed to process order.
:returns: ResultDTO
"""
successful, error_msg, can_retry = _ca_create_order(order_meta,
plugin_meta)
status = cert.CertificateStatus.CA_UNAVAILABLE_FOR_REQUEST
message = None
if successful:
status = cert.CertificateStatus.WAITING_FOR_CA
elif can_retry:
status = cert.CertificateStatus.CLIENT_DATA_ISSUE_SEEN
message = error_msg
return cert.ResultDTO(status=status, status_message=message)
def modify_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Update the order meta-data
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf.
:param barbican_meta_dto: additional data needed to process order.
"""
raise NotImplementedError # pragma: no cover
def cancel_certificate_request(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Cancel the order
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf.
:param barbican_meta_dto: additional data needed to process order.
"""
raise NotImplementedError # pragma: no cover
def check_certificate_status(self, order_id, order_meta, plugin_meta,
barbican_meta_dto):
"""Check status of the order
:param order_id: ID associated with the order
:param order_meta: Dict of meta-data associated with the order.
:param plugin_meta: Plugin meta-data previously set by calls to
this plugin. Plugins may also update/add
information here which Barbican will persist
on their behalf.
:param barbican_meta_dto: additional data needed to process order.
"""
raise NotImplementedError # pragma: no cover
def supports(self, certificate_spec):
"""Indicates if the plugin supports the certificate type.
:param certificate_spec: Contains details on the certificate to
generate the certificate order
:returns: boolean indicating if the plugin supports the certificate
type
"""
# TODO(chellygel): Research what certificate types are supported by
# symantec. Returning True for testing purposes
return True
def _ca_create_order(self, order_meta, plugin_meta):
"""Creates an order with the Symantec CA.
The PartnerOrderId and GeoTrustOrderId are returned and stored in
plugin_meta. PartnerCode and ProductCode are also stored in plugin_meta
for future use.
All required order parameters must be stored as a dict in
order_meta.
Required fields are:
PartnerCode, ProductCode, PartnerOrderId, OrganizationName,
AddressLine1, City, Region, PostalCode, Country, OrganizationPhone
ValidityPeriod, ServerCount, WebServerType, AdminContactFirstName,
AdminContactLastName, AdminContactPhone, AdminContactEmail,
AdminContactTitle, AdminContactAddressLine1, AdminContactCity,
AdminContactRegion, AdminContactPostalCode, AdminContactCountry,
BillingContact*, TechContact*, and CSR.
*The Billing and Tech contact information follows the same convention
as the AdminContact fields.
Optional Parameters: TechSameAsAdmin, BillSameAsAdmin, more options can be
found in Symantec's API docs. Contact Symantec for the API document.
:returns: tuple with success, error message, and can retry
"""
api = Symantec(self.username, self.password, self.url)
try:
order_data = api.order(**order_meta)
# GeotrustOrderId is used to handle emails from Symantec.
# PartnerCode and ProductCode are being stored in plugin_meta for
# convenience when calling _ca_get_order_status, _ca_modify_order, etc.
plugin_meta["GeotrustOrderID"] = order_data["GeotrustOrderID"]
plugin_meta["PartnerOrderID"] = order_data["PartnerOrderID"]
plugin_meta["PartnerCode"] = order_meta["OrderDetails"]["PartnerCode"]
plugin_meta["ProductCode"] = order_meta["OrderDetails"]["ProductCode"]
return True, None, False
except symantec_exceptions.SymantecError as e:
return False, e, False
except request_exceptions.RequestException as e:
return False, e, True
def _ca_get_order_status(self, plugin_meta):
"""Sends a request to the Symantec CA for details on an order.
Parameters needed for GetOrderByPartnerOrderID:
plugin_meta parameters: PartnerOrderId, PartnerCode
If the order is complete, the Certificate is returned as a string.
returns: tuple with success, error message, can retry,
and the certificate (if available).
"""
api = Symantec(self.username, self.password, self.url)
order_details = {
"PartnerOrderID": plugin_meta["PartnerOrderID"],
"PartnerCode": plugin_meta["PartnerCode"],
"ReturnCertificateInfo": "TRUE",
"ReturnFulfillment": "TRUE",
"ReturnCaCerts": "TRUE",
}
try:
order_data = api.get_order_by_partner_order_id(**order_details)
if order_data["OrderInfo"]["OrderState"] == "COMPLETED":
ca = order_data["Fulfillment"]["CACertificates"]["CACertificate"]
return True, None, False, ca["CACert"]
return True, None, False, None
except symantec_exceptions.SymantecError as e:
return False, e, False, None
except request_exceptions.RequestException as e:
return False, e, True, None
def _ca_modify_order(self, order_meta, plugin_meta):
"""Sends a request to the Symantec CA to modify an order.
Parameters needed for modifyOrder:
PartnerOrderID - Needed to specify order
PartnerCode - Needed to specify order
ProductCode - Needed to specify order
Also need a dict, order_meta with the parameters/values to modify.
returns: tuple with success, error message, and can retry.
"""
api = Symantec(self.username, self.password, self.url)
order_details = {
"PartnerOrderID": plugin_meta["PartnerOrderID"],
"PartnerCode": plugin_meta["PartnerCode"],
"ProductCode": plugin_meta["ProductCode"],
}
order_details.update(order_meta)
try:
api.validate_order_parameters(**order_details)
return True, None, False
except symantec_exceptions.SymantecError as e:
return False, e, False
except request_exceptions.RequestException as e:
return False, e, True
def _ca_cancel_order(self, plugin_meta):
"""Sends a request to the Symantec CA to cancel an order.
Parameters needed for modifyOrder:
PartnerOrderID - Needed to specify order
PartnerCode - Needed to specify order
ProductCode - Needed to specify order
returns: tuple with success, error message, and can retry.
"""
api = Symantec(self.username, self.password, self.url)
order_details = {
"PartnerOrderID": plugin_meta["PartnerOrderID"],
"PartnerCode": plugin_meta["PartnerCode"],
"ProductCode": plugin_meta["ProductCode"],
"ModifyOrderOperation": "CANCEL",
}
try:
api.modify_order(**order_details)
return True, None, False
except symantec_exceptions.SymantecError as e:
return False, e, False
except request_exceptions.RequestException as e:
return False, e, True