559 lines
18 KiB
Python
559 lines
18 KiB
Python
"""
|
|
.. module: lemur.pending_certificates.views
|
|
:platform: Unix
|
|
:license: Apache, see LICENSE for more details.
|
|
.. moduleauthor:: James Chuong <jchuong@instartlogic.com>
|
|
"""
|
|
from flask import Blueprint, g, make_response, jsonify
|
|
from flask_restful import Api, reqparse, inputs
|
|
|
|
from lemur.auth.service import AuthenticatedResource
|
|
from lemur.auth.permissions import CertificatePermission
|
|
|
|
from lemur.common.schema import validate_schema
|
|
from lemur.common.utils import paginated_parser
|
|
|
|
from lemur.pending_certificates import service
|
|
from lemur.roles import service as role_service
|
|
|
|
from lemur.pending_certificates.schemas import (
|
|
pending_certificate_output_schema,
|
|
pending_certificate_edit_input_schema,
|
|
pending_certificate_cancel_schema,
|
|
pending_certificate_upload_input_schema,
|
|
)
|
|
|
|
mod = Blueprint("pending_certificates", __name__)
|
|
api = Api(mod)
|
|
|
|
|
|
class PendingCertificatesList(AuthenticatedResource):
|
|
def __init__(self):
|
|
self.reqparse = reqparse.RequestParser()
|
|
super(PendingCertificatesList, self).__init__()
|
|
|
|
@validate_schema(None, pending_certificate_output_schema)
|
|
def get(self):
|
|
"""
|
|
.. http:get:: /pending_certificates
|
|
|
|
List of pending certificates
|
|
|
|
**Example request**:
|
|
|
|
.. sourcecode:: http
|
|
|
|
GET /pending_certificates 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
|
|
|
|
{
|
|
"status": null,
|
|
"cn": "*.test.example.net",
|
|
"chain": "",
|
|
"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",
|
|
"notBefore": "2016-06-03T00:00:00+00:00",
|
|
"notAfter": "2018-01-12T23:59:59+00:00",
|
|
"destinations": [],
|
|
"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"
|
|
}],
|
|
"rotation": true,
|
|
"rotationPolicy": {"name": "default"},
|
|
"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
|
|
}
|
|
|
|
:reqheader Authorization: OAuth token to authenticate
|
|
:statuscode 200: no error
|
|
:statuscode 403: unauthenticated
|
|
|
|
"""
|
|
parser = paginated_parser.copy()
|
|
parser.add_argument("timeRange", type=int, dest="time_range", location="args")
|
|
parser.add_argument("owner", type=inputs.boolean, location="args")
|
|
parser.add_argument("id", type=str, location="args")
|
|
parser.add_argument("active", type=inputs.boolean, location="args")
|
|
parser.add_argument(
|
|
"destinationId", type=int, dest="destination_id", location="args"
|
|
)
|
|
parser.add_argument("creator", type=str, location="args")
|
|
parser.add_argument("show", type=str, location="args")
|
|
args = parser.parse_args()
|
|
args["user"] = g.user
|
|
return service.render(args)
|
|
|
|
|
|
class PendingCertificates(AuthenticatedResource):
|
|
def __init__(self):
|
|
self.reqparse = reqparse.RequestParser()
|
|
super(PendingCertificates, self).__init__()
|
|
|
|
@validate_schema(None, pending_certificate_output_schema)
|
|
def get(self, pending_certificate_id):
|
|
"""
|
|
.. http:get:: /pending_certificates/1
|
|
|
|
One pending certificate
|
|
|
|
**Example request**:
|
|
|
|
.. sourcecode:: http
|
|
|
|
GET /pending_certificates/1 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
|
|
|
|
{
|
|
"status": null,
|
|
"cn": "*.test.example.net",
|
|
"chain": "",
|
|
"authority": {
|
|
"active": true,
|
|
"owner": "secure@example.com",
|
|
"id": 1,
|
|
"description": "verisign test authority",
|
|
"name": "verisign"
|
|
},
|
|
"owner": "joe@example.com",
|
|
"serial": "82311058732025924142789179368889309156",
|
|
"id": 1,
|
|
"issuer": "SymantecCorporation",
|
|
"notBefore": "2016-06-03T00:00:00+00:00",
|
|
"notAfter": "2018-01-12T23:59:59+00:00",
|
|
"destinations": [],
|
|
"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"
|
|
}],
|
|
"rotation": true,
|
|
"rotationPolicy": {"name": "default"},
|
|
"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
|
|
}
|
|
|
|
:reqheader Authorization: OAuth token to authenticate
|
|
:statuscode 200: no error
|
|
:statuscode 403: unauthenticated
|
|
|
|
"""
|
|
return service.get(pending_certificate_id)
|
|
|
|
@validate_schema(
|
|
pending_certificate_edit_input_schema, pending_certificate_output_schema
|
|
)
|
|
def put(self, pending_certificate_id, data=None):
|
|
"""
|
|
.. http:put:: /pending_certificates/1
|
|
|
|
Update a pending certificate
|
|
|
|
**Example request**:
|
|
|
|
.. sourcecode:: http
|
|
|
|
PUT /pending certificates/1 HTTP/1.1
|
|
Host: example.com
|
|
Accept: application/json, text/javascript
|
|
|
|
{
|
|
"owner": "jimbob@example.com",
|
|
"active": false
|
|
"notifications": [],
|
|
"destinations": [],
|
|
"replacements": []
|
|
}
|
|
|
|
**Example response**:
|
|
|
|
.. sourcecode:: http
|
|
|
|
HTTP/1.1 200 OK
|
|
Vary: Accept
|
|
Content-Type: text/javascript
|
|
|
|
{
|
|
"status": null,
|
|
"cn": "*.test.example.net",
|
|
"chain": "",
|
|
"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",
|
|
"destinations": [],
|
|
"description": null,
|
|
"deleted": null,
|
|
"notifications": [{
|
|
"id": 1
|
|
}]
|
|
"user": {
|
|
"username": "jane",
|
|
"active": true,
|
|
"email": "jane@example.com",
|
|
"id": 2
|
|
},
|
|
"active": true,
|
|
"number_attempts": 1,
|
|
"csr": "-----BEGIN CERTIFICATE REQUEST-----...",
|
|
"external_id": 12345,
|
|
"domains": [{
|
|
"sensitive": false,
|
|
"id": 1090,
|
|
"name": "*.test.example.net"
|
|
}],
|
|
"replaces": [],
|
|
"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"
|
|
}],
|
|
"rotation": true,
|
|
"rotationPolicy": {"name": "default"},
|
|
}
|
|
|
|
:reqheader Authorization: OAuth token to authenticate
|
|
:statuscode 200: no error
|
|
:statuscode 403: unauthenticated
|
|
|
|
"""
|
|
pending_cert = service.get(pending_certificate_id)
|
|
|
|
if not pending_cert:
|
|
return dict(message="Cannot find specified pending certificate"), 404
|
|
|
|
# allow creators
|
|
if g.current_user != pending_cert.user:
|
|
owner_role = role_service.get_by_name(pending_cert.owner)
|
|
permission = CertificatePermission(
|
|
owner_role, [x.name for x in pending_cert.roles]
|
|
)
|
|
|
|
if not permission.can():
|
|
return (
|
|
dict(message="You are not authorized to update this certificate"),
|
|
403,
|
|
)
|
|
|
|
for destination in data["destinations"]:
|
|
if destination.plugin.requires_key:
|
|
if not pending_cert.private_key:
|
|
return (
|
|
dict(
|
|
message="Unable to add destination: {0}. Certificate does not have required private key.".format(
|
|
destination.label
|
|
)
|
|
),
|
|
400,
|
|
)
|
|
|
|
pending_cert = service.update(pending_certificate_id, **data)
|
|
return pending_cert
|
|
|
|
@validate_schema(pending_certificate_cancel_schema, None)
|
|
def delete(self, pending_certificate_id, data=None):
|
|
"""
|
|
.. http:delete:: /pending_certificates/1
|
|
|
|
Cancel and delete a pending certificate
|
|
|
|
**Example request**:
|
|
|
|
.. sourcecode:: http
|
|
|
|
DELETE /pending certificates/1 HTTP/1.1
|
|
Host: example.com
|
|
Accept: application/json, text/javascript
|
|
|
|
{
|
|
"note": "Why I am cancelling this order"
|
|
}
|
|
|
|
**Example response**:
|
|
|
|
.. sourcecode:: http
|
|
|
|
HTTP/1.1 204 No Content
|
|
|
|
:reqheader Authorization: OAuth token to authenticate
|
|
:statuscode 204: no error
|
|
:statuscode 401: unauthenticated
|
|
:statuscode 403: unauthorized
|
|
:statuscode 404: pending certificate id not found
|
|
:statuscode 500: internal error
|
|
"""
|
|
pending_cert = service.get(pending_certificate_id)
|
|
|
|
if not pending_cert:
|
|
return dict(message="Cannot find specified pending certificate"), 404
|
|
|
|
# allow creators
|
|
if g.current_user != pending_cert.user:
|
|
owner_role = role_service.get_by_name(pending_cert.owner)
|
|
permission = CertificatePermission(
|
|
owner_role, [x.name for x in pending_cert.roles]
|
|
)
|
|
|
|
if not permission.can():
|
|
return (
|
|
dict(message="You are not authorized to update this certificate"),
|
|
403,
|
|
)
|
|
|
|
if service.cancel(pending_cert, **data):
|
|
service.delete(pending_cert)
|
|
return ("", 204)
|
|
else:
|
|
# service.cancel raises exception if there was an issue, but this will ensure something
|
|
# is relayed to user in case of something unexpected (unsuccessful update somehow).
|
|
return (
|
|
dict(
|
|
message="Unexpected error occurred while trying to cancel this certificate"
|
|
),
|
|
500,
|
|
)
|
|
|
|
|
|
class PendingCertificatePrivateKey(AuthenticatedResource):
|
|
def __init__(self):
|
|
super(PendingCertificatePrivateKey, self).__init__()
|
|
|
|
def get(self, pending_certificate_id):
|
|
"""
|
|
.. http:get:: /pending_certificates/1/key
|
|
|
|
Retrieves the private key for a given pneding certificate
|
|
|
|
**Example request**:
|
|
|
|
.. sourcecode:: http
|
|
|
|
GET /pending_certificates/1/key 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
|
|
|
|
{
|
|
"key": "-----BEGIN ..."
|
|
}
|
|
|
|
:reqheader Authorization: OAuth token to authenticate
|
|
:statuscode 200: no error
|
|
:statuscode 403: unauthenticated
|
|
"""
|
|
cert = service.get(pending_certificate_id)
|
|
if not cert:
|
|
return dict(message="Cannot find specified pending certificate"), 404
|
|
|
|
# allow creators
|
|
if g.current_user != cert.user:
|
|
owner_role = role_service.get_by_name(cert.owner)
|
|
permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
|
|
|
|
if not permission.can():
|
|
return dict(message="You are not authorized to view this key"), 403
|
|
|
|
response = make_response(jsonify(key=cert.private_key), 200)
|
|
response.headers["cache-control"] = "private, max-age=0, no-cache, no-store"
|
|
response.headers["pragma"] = "no-cache"
|
|
return response
|
|
|
|
|
|
class PendingCertificatesUpload(AuthenticatedResource):
|
|
""" Defines the 'pending_certificates' upload endpoint """
|
|
|
|
def __init__(self):
|
|
self.reqparse = reqparse.RequestParser()
|
|
super(PendingCertificatesUpload, self).__init__()
|
|
|
|
@validate_schema(
|
|
pending_certificate_upload_input_schema, pending_certificate_output_schema
|
|
)
|
|
def post(self, pending_certificate_id, data=None):
|
|
"""
|
|
.. http:post:: /pending_certificates/1/upload
|
|
|
|
Upload the body for a (signed) pending_certificate
|
|
|
|
**Example request**:
|
|
|
|
.. sourcecode:: http
|
|
|
|
POST /certificates/1/upload HTTP/1.1
|
|
Host: example.com
|
|
Accept: application/json, text/javascript
|
|
|
|
{
|
|
"body": "-----BEGIN CERTIFICATE-----...",
|
|
"chain": "-----BEGIN CERTIFICATE-----...",
|
|
}
|
|
|
|
**Example response**:
|
|
|
|
.. sourcecode:: http
|
|
|
|
HTTP/1.1 200 OK
|
|
Vary: Accept
|
|
Content-Type: text/javascript
|
|
|
|
{
|
|
"status": null,
|
|
"cn": "*.test.example.net",
|
|
"chain": "",
|
|
"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": [],
|
|
"rotation": true,
|
|
"rotationPolicy": {"name": "default"},
|
|
"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
|
|
}
|
|
|
|
:reqheader Authorization: OAuth token to authenticate
|
|
:statuscode 403: unauthenticated
|
|
:statuscode 200: no error
|
|
|
|
"""
|
|
return service.upload(pending_certificate_id, **data)
|
|
|
|
|
|
api.add_resource(
|
|
PendingCertificatesList, "/pending_certificates", endpoint="pending_certificates"
|
|
)
|
|
api.add_resource(
|
|
PendingCertificates,
|
|
"/pending_certificates/<int:pending_certificate_id>",
|
|
endpoint="pending_certificate",
|
|
)
|
|
api.add_resource(
|
|
PendingCertificatesUpload,
|
|
"/pending_certificates/<int:pending_certificate_id>/upload",
|
|
endpoint="pendingCertificateUpload",
|
|
)
|
|
api.add_resource(
|
|
PendingCertificatePrivateKey,
|
|
"/pending_certificates/<int:pending_certificate_id>/key",
|
|
endpoint="privateKeyPendingCertificates",
|
|
)
|