""" .. module: lemur.authorities.views :platform: Unix :copyright: (c) 2018 by Netflix Inc., see AUTHORS for more :license: Apache, see LICENSE for more details. .. moduleauthor:: Kevin Glisson """ from flask import Blueprint, g from flask_restful import reqparse, Api from lemur.common.utils import paginated_parser from lemur.common.schema import validate_schema from lemur.auth.service import AuthenticatedResource from lemur.auth.permissions import AuthorityPermission from lemur.certificates import service as certificate_service from lemur.authorities import service from lemur.authorities.schemas import ( authority_input_schema, authority_output_schema, authorities_output_schema, authority_update_schema, ) mod = Blueprint("authorities", __name__) api = Api(mod) class AuthoritiesList(AuthenticatedResource): """ Defines the 'authorities' endpoint """ def __init__(self): self.reqparse = reqparse.RequestParser() super(AuthoritiesList, self).__init__() @validate_schema(None, authorities_output_schema) def get(self): """ .. http:get:: /authorities The current list of authorities **Example request**: .. sourcecode:: http GET /authorities 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": [{ "name": "TestAuthority", "roles": [{ "id": 123, "name": "secure@example.com" }, { "id": 564, "name": "TestAuthority_admin" }, { "id": 565, "name": "TestAuthority_operator" }], "options": null, "active": true, "authorityCertificate": { "body": "-----BEGIN CERTIFICATE-----IyMzU5MTVaMHk...", "status": true, "cn": "AcommonName", "description": "This is the ROOT certificate for the TestAuthority certificate authority.", "chain": "", "notBefore": "2016-06-02T00:00:15+00:00", "notAfter": "2023-06-02T23:59:15+00:00", "owner": "secure@example.com", "user": { "username": "joe@example.com", "active": true, "email": "joe@example.com", "id": 3 }, "active": true, "bits": 2048, "id": 2235, "name": "TestAuthority" }, "owner": "secure@example.com", "id": 43, "description": "This is the ROOT certificate for the TestAuthority certificate authority." }], "total": 1 } :query sortBy: field to sort on :query sortDir: asc or desc :query page: int default is 1 :query filter: key value pair. format is k;v :query count: count number default is 10 :reqheader Authorization: OAuth token to authenticate :statuscode 200: no error :statuscode 403: unauthenticated :note: this will only show certificates that the current user is authorized to use """ parser = paginated_parser.copy() args = parser.parse_args() args["user"] = g.current_user return service.render(args) @validate_schema(authority_input_schema, authority_output_schema) def post(self, data=None): """ .. http:post:: /authorities Create an authority **Example request**: .. sourcecode:: http POST /authorities HTTP/1.1 Host: example.com Accept: application/json, text/javascript Content-Type: application/json;charset=UTF-8 { "country": "US", "state": "California", "location": "Los Gatos", "organization": "Netflix", "organizationalUnit": "Operations", "type": "root", "signingAlgorithm": "sha256WithRSA", "sensitivity": "medium", "keyType": "RSA2048", "plugin": { "slug": "cloudca-issuer" }, "name": "TimeTestAuthority5", "owner": "secure@example.com", "description": "test", "commonName": "AcommonName", "validityYears": "20", "extensions": { "subAltNames": { "names": [] }, "custom": [] } } **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Vary: Accept Content-Type: text/javascript { "name": "TestAuthority", "roles": [{ "id": 123, "name": "secure@example.com" }, { "id": 564, "name": "TestAuthority_admin" }, { "id": 565, "name": "TestAuthority_operator" }], "options": null, "active": true, "authorityCertificate": { "body": "-----BEGIN CERTIFICATE-----IyMzU5MTVaMHk...", "status": true, "cn": "AcommonName", "description": "This is the ROOT certificate for the TestAuthority certificate authority.", "chain": "", "notBefore": "2016-06-02T00:00:15+00:00", "notAfter": "2023-06-02T23:59:15+00:00", "owner": "secure@example.com", "user": { "username": "joe@example.com", "active": true, "email": "joe@example.com", "id": 3 }, "active": true, "bits": 2048, "id": 2235, "name": "TestAuthority" }, "owner": "secure@example.com", "id": 43, "description": "This is the ROOT certificate for the TestAuthority certificate authority." } :arg name: authority's name :arg description: a sensible description about what the CA with be used for :arg owner: the team or person who 'owns' this authority :arg validityStart: when this authority should start issuing certificates :arg validityEnd: when this authority should stop issuing certificates :arg validityYears: starting from `now` how many years into the future the authority should be valid :arg extensions: certificate extensions :arg plugin: name of the plugin to create the authority :arg type: the type of authority (root/subca) :arg parent: the parent authority if this is to be a subca :arg signingAlgorithm: algorithm used to sign the authority :arg keyType: key type :arg sensitivity: the sensitivity of the root key, for CloudCA this determines if the root keys are stored in an HSM :arg keyName: name of the key to store in the HSM (CloudCA) :arg serialNumber: serial number of the authority :arg firstSerial: specifies the starting serial number for certificates issued off of this authority :reqheader Authorization: OAuth token to authenticate :statuscode 403: unauthenticated :statuscode 200: no error """ data["creator"] = g.current_user return service.create(**data) class Authorities(AuthenticatedResource): def __init__(self): self.reqparse = reqparse.RequestParser() super(Authorities, self).__init__() @validate_schema(None, authority_output_schema) def get(self, authority_id): """ .. http:get:: /authorities/1 One authority **Example request**: .. sourcecode:: http GET /authorities/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 { "roles": [{ "id": 123, "name": "secure@example.com" }, { "id": 564, "name": "TestAuthority_admin" }, { "id": 565, "name": "TestAuthority_operator" }], "active": true, "owner": "secure@example.com", "id": 43, "description": "This is the ROOT certificate for the TestAuthority certificate authority." } :arg description: a sensible description about what the CA with be used for :arg owner: the team or person who 'owns' this authority :arg active: set whether this authoritity is currently in use :reqheader Authorization: OAuth token to authenticate :statuscode 403: unauthenticated :statuscode 200: no error :reqheader Authorization: OAuth token to authenticate :statuscode 200: no error :statuscode 403: unauthenticated """ return service.get(authority_id) @validate_schema(authority_update_schema, authority_output_schema) def put(self, authority_id, data=None): """ .. http:put:: /authorities/1 Update an authority **Example request**: .. sourcecode:: http PUT /authorities/1 HTTP/1.1 Host: example.com Accept: application/json, text/javascript Content-Type: application/json;charset=UTF-8 { "name": "TestAuthority5", "roles": [{ "id": 566, "name": "TestAuthority5_admin" }, { "id": 567, "name": "TestAuthority5_operator" }, { "id": 123, "name": "secure@example.com" }], "active": true, "authorityCertificate": { "body": "-----BEGIN CERTIFICATE-----", "status": null, "cn": "AcommonName", "description": "This is the ROOT certificate for the TestAuthority5 certificate authority.", "chain": "", "notBefore": "2016-06-03T00:00:51+00:00", "notAfter": "2036-06-03T23:59:51+00:00", "owner": "secure@example.com", "user": { "username": "joe@example.com", "active": true, "email": "joe@example.com", "id": 3 }, "active": true, "bits": 2048, "id": 2280, "name": "TestAuthority5" }, "owner": "secure@example.com", "id": 44, "description": "This is the ROOT certificate for the TestAuthority5 certificate authority." } **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Vary: Accept Content-Type: text/javascript { "name": "TestAuthority", "roles": [{ "id": 123, "name": "secure@example.com" }, { "id": 564, "name": "TestAuthority_admin" }, { "id": 565, "name": "TestAuthority_operator" }], "options": null, "active": true, "authorityCertificate": { "body": "-----BEGIN CERTIFICATE-----IyMzU5MTVaMHk...", "status": true, "cn": "AcommonName", "description": "This is the ROOT certificate for the TestAuthority certificate authority.", "chain": "", "notBefore": "2016-06-02T00:00:15+00:00", "notAfter": "2023-06-02T23:59:15+00:00", "owner": "secure@example.com", "user": { "username": "joe@example.com", "active": true, "email": "joe@example.com", "id": 3 }, "active": true, "bits": 2048, "id": 2235, "name": "TestAuthority" }, "owner": "secure@example.com", "id": 43, "description": "This is the ROOT certificate for the TestAuthority certificate authority." } :reqheader Authorization: OAuth token to authenticate :statuscode 200: no error :statuscode 403: unauthenticated """ authority = service.get(authority_id) if not authority: return dict(message="Not Found"), 404 # all the authority role members should be allowed roles = [x.name for x in authority.roles] permission = AuthorityPermission(authority_id, roles) if permission.can(): return service.update( authority_id, owner=data["owner"], description=data["description"], active=data["active"], roles=data["roles"], ) return dict(message="You are not authorized to update this authority."), 403 class CertificateAuthority(AuthenticatedResource): def __init__(self): super(CertificateAuthority, self).__init__() @validate_schema(None, authority_output_schema) def get(self, certificate_id): """ .. http:get:: /certificates/1/authority One authority for given certificate **Example request**: .. sourcecode:: http GET /certificates/1/authority 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 { "name": "TestAuthority", "roles": [{ "id": 123, "name": "secure@example.com" }, { "id": 564, "name": "TestAuthority_admin" }, { "id": 565, "name": "TestAuthority_operator" }], "options": null, "active": true, "authorityCertificate": { "body": "-----BEGIN CERTIFICATE-----IyMzU5MTVaMHk...", "status": true, "cn": "AcommonName", "description": "This is the ROOT certificate for the TestAuthority certificate authority.", "chain": "", "notBefore": "2016-06-02T00:00:15+00:00", "notAfter": "2023-06-02T23:59:15+00:00", "owner": "secure@example.com", "user": { "username": "joe@example.com", "active": true, "email": "joe@example.com", "id": 3 }, "active": true, "bits": 2048, "id": 2235, "name": "TestAuthority" }, "owner": "secure@example.com", "id": 43, "description": "This is the ROOT certificate for the TestAuthority certificate authority." } :reqheader Authorization: OAuth token to authenticate :statuscode 200: no error :statuscode 403: unauthenticated """ cert = certificate_service.get(certificate_id) if not cert: return dict(message="Certificate not found."), 404 return cert.authority class AuthorityVisualizations(AuthenticatedResource): def get(self, authority_id): """ .. http:get:: /authorities/1/visualize Authority visualization **Example request**: .. sourcecode:: http GET /certificates/1/visualize 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 {"name": "flare", "children": [ { "name": "analytics", "children": [ { "name": "cluster", "children": [ {"name": "AgglomerativeCluster", "size": 3938}, {"name": "CommunityStructure", "size": 3812}, {"name": "HierarchicalCluster", "size": 6714}, {"name": "MergeEdge", "size": 743} ] } ] } ] } :reqheader Authorization: OAuth token to authenticate :statuscode 200: no error :statuscode 403: unauthenticated """ authority = service.get(authority_id) return dict( name=authority.name, children=[{"name": c.name} for c in authority.certificates], ) api.add_resource(AuthoritiesList, "/authorities", endpoint="authorities") api.add_resource(Authorities, "/authorities/", endpoint="authority") api.add_resource( AuthorityVisualizations, "/authorities//visualize", endpoint="authority_visualizations", ) api.add_resource( CertificateAuthority, "/certificates//authority", endpoint="certificateAuthority", )