feat: allow admins to bypass approval flow when joining networks
This commit is contained in:
@@ -710,6 +710,128 @@ def admin_set_user_password(user_id):
|
||||
return api_response(data={"user": {"id": str(target.id), "email": target.email}}, message=f"Password updated for {target.email}")
|
||||
|
||||
|
||||
@api_v1_bp.route("/admin/users/<user_id>/ssh-certificates", methods=["GET"])
|
||||
@login_required
|
||||
@full_access_required
|
||||
def admin_get_user_ssh_certificates(user_id):
|
||||
"""List all SSH certificates for a user (admin view).
|
||||
|
||||
Returns all certificates — active, expired, revoked — with relevant
|
||||
metrics for admin visibility. Includes SSH key metadata (fingerprint,
|
||||
type, description) via the ssh_key relationship.
|
||||
|
||||
Query parameters:
|
||||
status: Filter by certificate status (issued, revoked, expired, superseded)
|
||||
active: If "true", return only currently valid certificates
|
||||
cert_type: Filter by certificate type (user, host)
|
||||
"""
|
||||
from gatehouse_app.models.ssh_ca.ssh_certificate import SSHCertificate, CertificateStatus
|
||||
from gatehouse_app.models.ssh_ca.ca import CertType
|
||||
|
||||
caller = g.current_user
|
||||
target = _find_user_for_admin(user_id)
|
||||
if not target:
|
||||
return api_response(success=False, message="User not found", status=404, error_type="NOT_FOUND")
|
||||
|
||||
if not _get_admin_access(caller, target):
|
||||
return api_response(success=False, message="Access denied", status=403, error_type="AUTHORIZATION_ERROR")
|
||||
|
||||
query = SSHCertificate.query.filter_by(user_id=user_id, deleted_at=None)
|
||||
|
||||
# Filter by explicit status (e.g. ?status=revoked)
|
||||
status_param = request.args.get("status", "").strip().lower()
|
||||
if status_param:
|
||||
try:
|
||||
status_enum = CertificateStatus(status_param)
|
||||
query = query.filter(SSHCertificate.status == status_enum)
|
||||
except ValueError:
|
||||
valid_statuses = [s.value for s in CertificateStatus]
|
||||
return api_response(
|
||||
success=False,
|
||||
message=f"Invalid status '{status_param}'. Must be one of: {', '.join(valid_statuses)}",
|
||||
status=400, error_type="VALIDATION_ERROR",
|
||||
)
|
||||
|
||||
# Filter for only currently valid certs (?active=true)
|
||||
active_param = request.args.get("active", "").strip().lower()
|
||||
if active_param == "true":
|
||||
now = datetime.now(timezone.utc)
|
||||
query = query.filter(
|
||||
SSHCertificate.revoked == False,
|
||||
SSHCertificate.valid_after <= now,
|
||||
SSHCertificate.valid_before >= now,
|
||||
)
|
||||
elif active_param == "false":
|
||||
now = datetime.now(timezone.utc)
|
||||
query = query.filter(
|
||||
(SSHCertificate.revoked == True) |
|
||||
(SSHCertificate.valid_before < now)
|
||||
)
|
||||
|
||||
# Filter by certificate type (?cert_type=host)
|
||||
cert_type_param = request.args.get("cert_type", "").strip().lower()
|
||||
if cert_type_param:
|
||||
try:
|
||||
cert_type_enum = CertType(cert_type_param)
|
||||
query = query.filter(SSHCertificate.cert_type == cert_type_enum)
|
||||
except ValueError:
|
||||
return api_response(
|
||||
success=False,
|
||||
message=f"Invalid cert_type '{cert_type_param}'. Must be one of: user, host",
|
||||
status=400, error_type="VALIDATION_ERROR",
|
||||
)
|
||||
|
||||
# Pagination
|
||||
try:
|
||||
page = max(1, int(request.args.get("page", 1)))
|
||||
per_page = min(100, max(1, int(request.args.get("per_page", 50))))
|
||||
except ValueError:
|
||||
page, per_page = 1, 50
|
||||
|
||||
total = query.count()
|
||||
certs = (
|
||||
query.order_by(SSHCertificate.created_at.desc())
|
||||
.offset((page - 1) * per_page)
|
||||
.limit(per_page)
|
||||
.all()
|
||||
)
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
certs_data = []
|
||||
for cert in certs:
|
||||
d = cert.to_dict()
|
||||
# Enrich with SSH key metadata
|
||||
if cert.ssh_key:
|
||||
d["ssh_key"] = {
|
||||
"id": str(cert.ssh_key.id),
|
||||
"fingerprint": cert.ssh_key.fingerprint,
|
||||
"key_type": cert.ssh_key.key_type,
|
||||
"key_bits": cert.ssh_key.key_bits,
|
||||
"key_comment": cert.ssh_key.key_comment,
|
||||
"description": cert.ssh_key.description,
|
||||
"verified": cert.ssh_key.verified,
|
||||
}
|
||||
else:
|
||||
d["ssh_key"] = None
|
||||
certs_data.append(d)
|
||||
|
||||
return api_response(
|
||||
data={
|
||||
"user": {
|
||||
"id": str(target.id),
|
||||
"email": target.email,
|
||||
"full_name": target.full_name,
|
||||
},
|
||||
"certificates": certs_data,
|
||||
"count": total,
|
||||
"page": page,
|
||||
"per_page": per_page,
|
||||
"pages": (total + per_page - 1) // per_page,
|
||||
},
|
||||
message="SSH certificates retrieved successfully",
|
||||
)
|
||||
|
||||
|
||||
@api_v1_bp.route("/admin/users/<user_id>/linked-accounts", methods=["GET"])
|
||||
@login_required
|
||||
@full_access_required
|
||||
|
||||
Reference in New Issue
Block a user