支持删除CA和证书

This commit is contained in:
wzj 2025-06-14 11:10:16 +08:00
parent dac0655cff
commit c390c63323
5 changed files with 216 additions and 0 deletions

121
app.py
View File

@ -1049,6 +1049,127 @@ def export_certificate_view(cert_id):
return render_template('export_certificate.html', cert=cert)
# 在app.py中添加以下路由
@app.route('/cas/<int:ca_id>/delete', methods=['GET', 'POST'])
@login_required
def delete_ca(ca_id):
ca = get_ca_by_id(ca_id)
if not ca:
flash('CA不存在', 'danger')
return redirect(url_for('ca_list'))
# 检查权限
if not current_user.is_admin and ca['created_by'] != current_user.id:
flash('无权删除此CA', 'danger')
return redirect(url_for('ca_list'))
# 检查是否有关联的证书
conn = get_db_connection()
if conn:
try:
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT COUNT(*) as count FROM certificates WHERE ca_id = %s", (ca_id,))
result = cursor.fetchone()
if result['count'] > 0:
flash('无法删除CA因为存在关联的证书', 'danger')
return redirect(url_for('ca_detail', ca_id=ca_id))
except Error as e:
print(f"Database error: {e}")
flash('检查关联证书失败', 'danger')
return redirect(url_for('ca_detail', ca_id=ca_id))
finally:
if conn.is_connected():
cursor.close()
conn.close()
if request.method == 'POST':
# 删除文件
try:
if os.path.exists(ca['cert_path']):
os.remove(ca['cert_path'])
if os.path.exists(ca['key_path']):
os.remove(ca['key_path'])
# 删除CA目录
ca_dir = os.path.dirname(ca['cert_path'])
if os.path.exists(ca_dir):
os.rmdir(ca_dir)
except OSError as e:
print(f"文件删除错误: {e}")
flash('删除文件时出错', 'danger')
return redirect(url_for('ca_detail', ca_id=ca_id))
# 删除数据库记录
conn = get_db_connection()
if conn:
try:
cursor = conn.cursor()
cursor.execute("DELETE FROM certificate_authorities WHERE id = %s", (ca_id,))
conn.commit()
flash('CA删除成功', 'success')
return redirect(url_for('ca_list'))
except Error as e:
print(f"Database error: {e}")
flash('删除CA记录失败', 'danger')
return redirect(url_for('ca_detail', ca_id=ca_id))
finally:
if conn.is_connected():
cursor.close()
conn.close()
return render_template('confirm_delete_ca.html', ca=ca)
@app.route('/certificates/<int:cert_id>/delete', methods=['GET', 'POST'])
@login_required
def delete_certificate(cert_id):
cert = get_certificate_by_id(cert_id)
if not cert:
flash('证书不存在', 'danger')
return redirect(url_for('certificate_list'))
# 检查权限
if not current_user.is_admin and cert['created_by'] != current_user.id:
flash('无权删除此证书', 'danger')
return redirect(url_for('certificate_list'))
if request.method == 'POST':
# 删除文件
try:
if os.path.exists(cert['cert_path']):
os.remove(cert['cert_path'])
if os.path.exists(cert['key_path']):
os.remove(cert['key_path'])
if os.path.exists(cert['csr_path']):
os.remove(cert['csr_path'])
# 删除证书目录
cert_dir = os.path.dirname(cert['cert_path'])
if os.path.exists(cert_dir):
os.rmdir(cert_dir)
except OSError as e:
print(f"文件删除错误: {e}")
flash('删除文件时出错', 'danger')
return redirect(url_for('certificate_detail', cert_id=cert_id))
# 删除数据库记录
conn = get_db_connection()
if conn:
try:
cursor = conn.cursor()
cursor.execute("DELETE FROM certificates WHERE id = %s", (cert_id,))
conn.commit()
flash('证书删除成功', 'success')
return redirect(url_for('certificate_list'))
except Error as e:
print(f"Database error: {e}")
flash('删除证书记录失败', 'danger')
return redirect(url_for('certificate_detail', cert_id=cert_id))
finally:
if conn.is_connected():
cursor.close()
conn.close()
return render_template('confirm_delete_certificate.html', cert=cert)
def generate_separate_files_zip(cert, cert_ext, zip_suffix):
"""生成包含分开文件的ZIP包通用函数"""

View File

@ -28,6 +28,12 @@
<i class="fas fa-download me-1"></i> 下载CRL
</a>
{% endif %}
<a href="{{ url_for('delete_ca', ca_id=ca.id) }}"
class="btn btn-danger me-2"
data-bs-toggle="tooltip"
title="删除此CA(谨慎操作)">
<i class="fas fa-trash-alt me-1"></i> 删除CA
</a>
</div>
</div>

View File

@ -11,6 +11,7 @@
{% endif %}
<a href="{{ url_for('renew_certificate_view', cert_id=cert.id) }}" class="btn btn-primary me-2">续期</a>
<a href="{{ url_for('export_certificate_view', cert_id=cert.id) }}" class="btn btn-success">导出</a>
<a href="{{ url_for('delete_certificate', cert_id=cert.id) }}" class="btn btn-danger">删除</a>
</div>
</div>

View File

@ -0,0 +1,40 @@
{% extends "base.html" %}
{% block title %}删除CA - {{ ca.name }}{% endblock %}
{% block content %}
<div class="container mt-4">
<div class="card border-danger">
<div class="card-header bg-danger text-white">
<h4><i class="fas fa-exclamation-triangle me-2"></i> 确认删除CA</h4>
</div>
<div class="card-body">
<h5 class="card-title">您确定要删除以下CA吗</h5>
<div class="alert alert-danger">
<strong>警告:</strong>此操作不可逆所有与此CA相关的文件将被永久删除。
</div>
<div class="mb-4">
<h6>CA信息</h6>
<ul>
<li>名称: {{ ca.name }}</li>
<li>通用名: {{ ca.common_name }}</li>
<li>组织: {{ ca.organization }}</li>
<li>创建时间: {{ ca.created_at.strftime('%Y-%m-%d %H:%M') }}</li>
</ul>
</div>
<form method="POST" action="{{ url_for('delete_ca', ca_id=ca.id) }}">
<div class="d-flex justify-content-between">
<a href="{{ url_for('ca_detail', ca_id=ca.id) }}" class="btn btn-secondary">
<i class="fas fa-times me-1"></i> 取消
</a>
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash-alt me-1"></i> 确认删除
</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,48 @@
{% extends "base.html" %}
{% block title %}删除证书 - {{ cert.common_name }}{% endblock %}
{% block content %}
<div class="container mt-4">
<div class="card border-danger">
<div class="card-header bg-danger text-white">
<h4><i class="fas fa-exclamation-triangle me-2"></i> 确认删除证书</h4>
</div>
<div class="card-body">
<h5 class="card-title">您确定要删除以下证书吗?</h5>
<div class="alert alert-danger">
<strong>警告:</strong>此操作不可逆!所有与此证书相关的文件将被永久删除。
</div>
<div class="mb-4">
<h6>证书信息:</h6>
<ul>
<li>通用名: {{ cert.common_name }}</li>
<li>颁发CA: {{ cert.ca_name }}</li>
<li>状态:
{% if cert.status == 'active' %}
<span class="badge bg-success">有效</span>
{% elif cert.status == 'revoked' %}
<span class="badge bg-danger">已吊销</span>
{% else %}
<span class="badge bg-secondary">已过期</span>
{% endif %}
</li>
<li>创建时间: {{ cert.created_at.strftime('%Y-%m-%d %H:%M') }}</li>
</ul>
</div>
<form method="POST" action="{{ url_for('delete_certificate', cert_id=cert.id) }}">
<div class="d-flex justify-content-between">
<a href="{{ url_for('certificate_detail', cert_id=cert.id) }}" class="btn btn-secondary">
<i class="fas fa-times me-1"></i> 取消
</a>
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash-alt me-1"></i> 确认删除
</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}