Add ACME certificate revoke and delete gates
This commit is contained in:
+113
-4
@@ -4,11 +4,120 @@ Let's Encrypt certificate delete/revoke gates.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from functions.common import log
|
||||||
|
from functions.common import replace_exactly_once
|
||||||
|
|
||||||
|
|
||||||
|
DELETE_FUNCTION_ANCHOR = '''\tdelete: async (access, data) => {
|
||||||
|
\t\tawait access.can("certificates:delete", data.id);
|
||||||
|
\t\tconst row = await internalCertificate.get(access, { id: data.id });
|
||||||
|
|
||||||
|
\t\tif (!row?.id) {
|
||||||
|
\t\t\tthrow new error.ItemNotFoundError(data.id);
|
||||||
|
\t\t}
|
||||||
|
'''
|
||||||
|
|
||||||
|
DELETE_GATE_FRAGMENT = '''\tdelete: async (access, data) => {
|
||||||
|
\t\tawait access.can("certificates:delete", data.id);
|
||||||
|
\t\tconst row = await internalCertificate.get(access, { id: data.id });
|
||||||
|
|
||||||
|
\t\tif (!row?.id) {
|
||||||
|
\t\t\tthrow new error.ItemNotFoundError(data.id);
|
||||||
|
\t\t}
|
||||||
|
|
||||||
|
\t\tif (row.provider === "letsencrypt") {
|
||||||
|
\t\t\trequireAcmeAuthority();
|
||||||
|
\t\t}
|
||||||
|
'''
|
||||||
|
|
||||||
|
REVOKE_FUNCTION_ANCHOR = '''\trevokeLetsEncryptSsl: async (certificate, throwErrors) => {
|
||||||
|
'''
|
||||||
|
|
||||||
|
REVOKE_GATE_FRAGMENT = '''\trevokeLetsEncryptSsl: async (certificate, throwErrors) => {
|
||||||
|
\t\trequireAcmeAuthority();
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
def apply(source: str) -> str:
|
def apply(source: str) -> str:
|
||||||
"""
|
"""
|
||||||
Install both:
|
Install both Let's Encrypt revoke protections:
|
||||||
- delete() pre-database-mutation authority gate
|
|
||||||
- revokeLetsEncryptSsl() execution authority gate
|
1. delete() must require authority before mutating the DB row.
|
||||||
|
2. revokeLetsEncryptSsl() requires authority again immediately before
|
||||||
|
the Certbot revocation path.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("revoke gate not implemented yet")
|
|
||||||
|
if DELETE_GATE_FRAGMENT in source:
|
||||||
|
log("delete_gate=already-present")
|
||||||
|
else:
|
||||||
|
source = replace_exactly_once(
|
||||||
|
source,
|
||||||
|
DELETE_FUNCTION_ANCHOR,
|
||||||
|
DELETE_GATE_FRAGMENT,
|
||||||
|
description="delete gate",
|
||||||
|
)
|
||||||
|
log("delete_gate=added")
|
||||||
|
|
||||||
|
if REVOKE_GATE_FRAGMENT in source:
|
||||||
|
log("revoke_gate=already-present")
|
||||||
|
else:
|
||||||
|
source = replace_exactly_once(
|
||||||
|
source,
|
||||||
|
REVOKE_FUNCTION_ANCHOR,
|
||||||
|
REVOKE_GATE_FRAGMENT,
|
||||||
|
description="revoke gate",
|
||||||
|
)
|
||||||
|
log("revoke_gate=added")
|
||||||
|
|
||||||
|
return source
|
||||||
|
|
||||||
|
|
||||||
|
def verify(source: str) -> None:
|
||||||
|
"""
|
||||||
|
Verify both delete and Certbot revoke gates.
|
||||||
|
|
||||||
|
Also verify that the delete gate appears before the database mutation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if DELETE_GATE_FRAGMENT not in source:
|
||||||
|
raise RuntimeError(
|
||||||
|
"revoke gate verification: delete() authority check not found"
|
||||||
|
)
|
||||||
|
|
||||||
|
if REVOKE_GATE_FRAGMENT not in source:
|
||||||
|
raise RuntimeError(
|
||||||
|
"revoke gate verification: revokeLetsEncryptSsl() "
|
||||||
|
"authority check not found"
|
||||||
|
)
|
||||||
|
|
||||||
|
delete_gate_index = source.index(DELETE_GATE_FRAGMENT)
|
||||||
|
|
||||||
|
delete_db_anchor = '''certificateModel.query().where("id", row.id).patch({
|
||||||
|
\t\t\tis_deleted: 1,
|
||||||
|
\t\t});'''
|
||||||
|
|
||||||
|
delete_db_index = source.find(
|
||||||
|
delete_db_anchor,
|
||||||
|
delete_gate_index,
|
||||||
|
)
|
||||||
|
|
||||||
|
if delete_db_index == -1:
|
||||||
|
raise RuntimeError(
|
||||||
|
"revoke gate verification: certificate delete DB mutation "
|
||||||
|
"not found after authority gate"
|
||||||
|
)
|
||||||
|
|
||||||
|
revoke_index = source.index(REVOKE_GATE_FRAGMENT)
|
||||||
|
|
||||||
|
certbot_revoke_index = source.find(
|
||||||
|
'"revoke"',
|
||||||
|
revoke_index,
|
||||||
|
)
|
||||||
|
|
||||||
|
if certbot_revoke_index == -1:
|
||||||
|
raise RuntimeError(
|
||||||
|
"revoke gate verification: Certbot revoke operation "
|
||||||
|
"not found after authority gate"
|
||||||
|
)
|
||||||
|
|
||||||
|
log("revoke_gate_verification=passed")
|
||||||
|
|||||||
Reference in New Issue
Block a user