adding a new API for faster certificate lookup.

The new API api/1/certificates/valid returns only non-expired (not_after >= today) certs which have auto-rotate enabled:

cn is a required parameter:

http://localhost:8000/api/1/certificates/valid?filter=cn;example.com
cn can also be a database string wildcard ('%'):

http://localhost:8000/api/1/certificates/valid?filter=cn;%
owner is the additional parameter, and must be the email address of the owner:

http://localhost:8000/api/1/certificates/valid?filter=cn;example.com&owner=hossein@example.com
given owner  and a database string wildcard ('%') one can retrieve all certs for that owner, which are still valid, and have auto-rotate enabled:

http://localhost:8000/api/1/certificates/valid?filter=cn;%&owner=hossein@example.com
This commit is contained in:
Hossein Shafagh 2019-05-11 18:06:51 -07:00
parent 0f2773c986
commit f452a7ce68
3 changed files with 122 additions and 0 deletions

View File

@ -401,6 +401,30 @@ def query_name(certificate_name, args):
return result
def query_common_name(common_name, args):
"""
Helper function that queries for not expired certificates by common name and owner which have auto-rotate enabled
:param common_name:
:param args:
:return:
"""
owner = args.pop('owner')
if not owner:
owner = '%'
# only not expired certificates
current_time = arrow.utcnow()
result = Certificate.query.filter(Certificate.cn.ilike(common_name)) \
.filter(Certificate.owner.ilike(owner))\
.filter(Certificate.not_after >= current_time.format('YYYY-MM-DD')) \
.filter(Certificate.rotation.is_(True))\
.all()
return result
def create_csr(**csr_config):
"""
Given a list of domains create the appropriate csr

View File

@ -37,6 +37,102 @@ mod = Blueprint('certificates', __name__)
api = Api(mod)
class CertificatesListValid(AuthenticatedResource):
""" Defines the 'certificates/valid' endpoint """
def __init__(self):
self.reqparse = reqparse.RequestParser()
super(CertificatesListValid, self).__init__()
@validate_schema(None, certificates_output_schema)
def get(self):
"""
.. http:get:: /certificates/valid/<query>
The current list of not-expired certificates for a given common name, and owner
**Example request**:
.. sourcecode:: http
GET /certificates/valid?filter=cn;*.test.example.net&owner=joe@example.com
HTTP/1.1
Host: example.com
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"items": [{
"status": null,
"cn": "*.test.example.net",
"chain": "",
"csr": "-----BEGIN CERTIFICATE REQUEST-----"
"authority": {
"active": true,
"owner": "secure@example.com",
"id": 1,
"description": "verisign test authority",
"name": "verisign"
},
"owner": "joe@example.com",
"serial": "82311058732025924142789179368889309156",
"id": 2288,
"issuer": "SymantecCorporation",
"dateCreated": "2016-06-03T06:09:42.133769+00:00",
"notBefore": "2016-06-03T00:00:00+00:00",
"notAfter": "2018-01-12T23:59:59+00:00",
"destinations": [],
"bits": 2048,
"body": "-----BEGIN CERTIFICATE-----...",
"description": null,
"deleted": null,
"notifications": [{
"id": 1
}],
"signingAlgorithm": "sha256",
"user": {
"username": "jane",
"active": true,
"email": "jane@example.com",
"id": 2
},
"active": true,
"domains": [{
"sensitive": false,
"id": 1090,
"name": "*.test.example.net"
}],
"replaces": [],
"replaced": [],
"name": "WILDCARD.test.example.net-SymantecCorporation-20160603-20180112",
"roles": [{
"id": 464,
"description": "This is a google group based role created by Lemur",
"name": "joe@example.com"
}],
"san": null
}],
"total": 1
}
:reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error
:statuscode 403: unauthenticated
"""
parser = paginated_parser.copy()
args = parser.parse_args()
args['user'] = g.user
common_name = args['filter'].split(';')[1]
return service.query_common_name(common_name, args)
class CertificatesNameQuery(AuthenticatedResource):
""" Defines the 'certificates/name' endpoint """
@ -1190,6 +1286,7 @@ class CertificateRevoke(AuthenticatedResource):
api.add_resource(CertificateRevoke, '/certificates/<int:certificate_id>/revoke', endpoint='revokeCertificate')
api.add_resource(CertificatesNameQuery, '/certificates/name/<string:certificate_name>', endpoint='certificatesNameQuery')
api.add_resource(CertificatesList, '/certificates', endpoint='certificates')
api.add_resource(CertificatesListValid, '/certificates/valid', endpoint='certificatesListValid')
api.add_resource(Certificates, '/certificates/<int:certificate_id>', endpoint='certificate')
api.add_resource(CertificatesStats, '/certificates/stats', endpoint='certificateStats')
api.add_resource(CertificatesUpload, '/certificates/upload', endpoint='certificateUpload')

View File

@ -30,6 +30,7 @@ paginated_parser.add_argument('page', type=int, default=1, location='args')
paginated_parser.add_argument('sortDir', type=str, dest='sort_dir', location='args')
paginated_parser.add_argument('sortBy', type=str, dest='sort_by', location='args')
paginated_parser.add_argument('filter', type=str, location='args')
paginated_parser.add_argument('owner', type=str, location='args')
def get_psuedo_random_string():