Service for AgentIdentity full PKI lifecycle management. PKI Flow: 1. submit_csr() - Agent submits CSR (public key + metadata) 2. issue_from_csr() - Org CA validates + signs -> AgentCertificate + identity 3. verify() - Static verification (s
| 167 | |
| 168 | |
| 169 | class AgentIdentityService: |
| 170 | """ |
| 171 | Service for AgentIdentity full PKI lifecycle management. |
| 172 | |
| 173 | PKI Flow: |
| 174 | 1. submit_csr() - Agent submits CSR (public key + metadata) |
| 175 | 2. issue_from_csr() - Org CA validates + signs -> AgentCertificate + identity |
| 176 | 3. verify() - Static verification (status + expiry) |
| 177 | 4. request_challenge() - Verifier requests a challenge |
| 178 | 5. respond_challenge() - Agent signs nonce (proves key possession) |
| 179 | 6. verify_challenge() - Verifier validates signature |
| 180 | 7. revoke() - Org authority revokes identity |
| 181 | """ |
| 182 | |
| 183 | def __init__(self, db: AsyncSession) -> None: |
| 184 | self._db = db |
| 185 | |
| 186 | # ── Step 1: CSR Submission ────────────────────────────────────────────── |
| 187 | |
| 188 | async def submit_csr(self, csr: AgentIdentityCSR) -> AgentIdentityCSR: |
| 189 | """ |
| 190 | Validate and store a CSR for later signing. |
| 191 | |
| 192 | Step 1 of the PKI lifecycle: Agent submits CSR with public key + metadata. |
| 193 | The CSR is validated (duplicate check, key format) and queued for signing. |
| 194 | The private key is NEVER transmitted — only the public key. |
| 195 | |
| 196 | Args: |
| 197 | csr: CSR from the agent. |
| 198 | |
| 199 | Returns: |
| 200 | The validated CSR with assigned csr_id. |
| 201 | |
| 202 | Raises: |
| 203 | ValueError: If agent_name already exists in org. |
| 204 | """ |
| 205 | existing = await self._db.execute( |
| 206 | select(AgentIdentityModel).where( |
| 207 | AgentIdentityModel.agent_name == csr.agent_name, |
| 208 | AgentIdentityModel.org_id == csr.org_id, |
| 209 | ) |
| 210 | ) |
| 211 | if existing.scalars().first() is not None: |
| 212 | raise ValueError( |
| 213 | f"AgentIdentity already exists for agent_name={csr.agent_name} in org={csr.org_id}" |
| 214 | ) |
| 215 | return csr |
| 216 | |
| 217 | # ── Step 2: Issue from CSR (CA Signing) ──────────────────────────────── |
| 218 | |
| 219 | async def issue_from_csr(self, csr: AgentIdentityCSR) -> AgentIdentityIssueResult: |
| 220 | """ |
| 221 | Issue an AgentIdentity from a validated CSR — Org CA signing. |
| 222 | |
| 223 | Step 2-3 of the PKI lifecycle: |
| 224 | 2. Org CA validates the CSR request |
| 225 | 3. Signs and issues AgentCertificate |
| 226 |
no outgoing calls
no test coverage detected