Skip to content

Commit

Permalink
NAS-132865 / 25.04 / Clean up old validation for keychaincredential
Browse files Browse the repository at this point in the history
…, make API definitions more precise (#15392)

* improve readability

* begin replacing old validation

* more precise schema

* remove old schemas

* try abc entry

* clarify error message
  • Loading branch information
creatorcary authored Jan 16, 2025
1 parent e6b7bc4 commit 237a19c
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 145 deletions.
110 changes: 83 additions & 27 deletions src/middlewared/middlewared/api/v25_04_0/keychain.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import abc
from typing import Literal

from pydantic import Field, Secret
from pydantic import Secret

from middlewared.api.base import (BaseModel, Excluded, excluded_field, ForUpdateMetaclass, HttpUrl, LongString,
NonEmptyString, single_argument_args, single_argument_result)
Expand All @@ -18,27 +19,71 @@
"KeychainCredentialSetupSSHConnectionArgs", "KeychainCredentialSetupSSHConnectionResult"]


class KeychainCredentialEntry(BaseModel):
class SSHKeyPair(BaseModel):
"""At least one of the two keys must be provided on creation."""
private_key: LongString | None = None
public_key: LongString | None = None
"""Can be omitted and automatically derived from the private key."""


class SSHCredentials(BaseModel):
host: str
port: int = 22
username: str = "root"
private_key: int
"""Keychain Credential ID."""
remote_host_key: str
"""Can be discovered with keychaincredential.remote_ssh_host_key_scan."""
connect_timeout: int = 10


class KeychainCredentialEntry(BaseModel, abc.ABC):
id: int
name: NonEmptyString
type: str
attributes: Secret[dict]
"""Distinguishes this Keychain Credential from others."""
type: Literal["SSH_KEY_PAIR", "SSH_CREDENTIALS"]
attributes: Secret[SSHKeyPair | SSHCredentials]


class SSHKeyPairEntry(KeychainCredentialEntry):
type: Literal["SSH_KEY_PAIR"]
attributes: Secret[SSHKeyPair]


class SSHCredentialsEntry(KeychainCredentialEntry):
type: Literal["SSH_CREDENTIALS"]
attributes: Secret[SSHCredentials]

class KeychainCredentialCreate(KeychainCredentialEntry):

class KeychainCredentialCreateSSHKeyPairEntry(SSHKeyPairEntry):
id: Excluded = excluded_field()


class KeychainCredentialUpdate(KeychainCredentialCreate, metaclass=ForUpdateMetaclass):
class KeychainCredentialUpdateSSHKeyPairEntry(KeychainCredentialCreateSSHKeyPairEntry, metaclass=ForUpdateMetaclass):
type: Excluded = excluded_field()


class KeychainCredentialCreateSSHCredentialsEntry(SSHCredentialsEntry):
id: Excluded = excluded_field()


class KeychainCredentialUpdateSSHCredentialsEntry(
KeychainCredentialCreateSSHCredentialsEntry,
metaclass=ForUpdateMetaclass
):
type: Excluded = excluded_field()


KeychainCredentialCreate = KeychainCredentialCreateSSHKeyPairEntry | KeychainCredentialCreateSSHCredentialsEntry
KeychainCredentialUpdate = KeychainCredentialUpdateSSHKeyPairEntry | KeychainCredentialUpdateSSHCredentialsEntry


class KeychainCredentialCreateArgs(BaseModel):
keychain_credential_create: KeychainCredentialCreate


class KeychainCredentialCreateResult(BaseModel):
result: KeychainCredentialEntry
result: SSHKeyPairEntry | SSHCredentialsEntry


class KeychainCredentialUpdateArgs(BaseModel):
Expand All @@ -47,7 +92,7 @@ class KeychainCredentialUpdateArgs(BaseModel):


class KeychainCredentialUpdateResult(BaseModel):
result: KeychainCredentialEntry
result: SSHKeyPairEntry | SSHCredentialsEntry


class KeychainCredentialDeleteOptions(BaseModel):
Expand All @@ -56,7 +101,7 @@ class KeychainCredentialDeleteOptions(BaseModel):

class KeychainCredentialDeleteArgs(BaseModel):
id: int
options: KeychainCredentialDeleteOptions = Field(default=KeychainCredentialDeleteOptions())
options: KeychainCredentialDeleteOptions = KeychainCredentialDeleteOptions()


class KeychainCredentialDeleteResult(BaseModel):
Expand All @@ -78,12 +123,11 @@ class KeychainCredentialUsedByResult(BaseModel):

class KeychainCredentialGetOfTypeArgs(BaseModel):
id: int
type: str
type: Literal["SSH_KEY_PAIR", "SSH_CREDENTIALS"]


@single_argument_result
class KeychainCredentialGetOfTypeResult(KeychainCredentialEntry):
pass
class KeychainCredentialGetOfTypeResult(BaseModel):
result: SSHKeyPairEntry | SSHCredentialsEntry


class KeychainCredentialGenerateSSHKeyPairArgs(BaseModel):
Expand All @@ -107,8 +151,7 @@ class KeychainCredentialRemoteSSHHostKeyScanResult(BaseModel):
result: str


@single_argument_args("keychain_remote_ssh_semiautomatic_setup")
class KeychainCredentialRemoteSSHSemiautomaticSetupArgs(BaseModel):
class KeychainCredentialRemoteSSHSemiautomaticSetup(BaseModel):
name: NonEmptyString
url: HttpUrl
verify_ssl: bool = True
Expand All @@ -122,8 +165,12 @@ class KeychainCredentialRemoteSSHSemiautomaticSetupArgs(BaseModel):
sudo: bool = False


class KeychainCredentialRemoteSSHSemiautomaticSetupArgs(BaseModel):
data: KeychainCredentialRemoteSSHSemiautomaticSetup


class KeychainCredentialRemoteSSHSemiautomaticSetupResult(BaseModel):
result: KeychainCredentialEntry
result: SSHCredentialsEntry


@single_argument_args("keychain_ssh_pair")
Expand All @@ -149,22 +196,31 @@ class KeychainCredentialSetupSSHConnectionKeyExisting(BaseModel):
existing_key_id: int


class KeychainCredentialSetupSSHConnectionSemiAutomaticSetup(
KeychainCredentialRemoteSSHSemiautomaticSetupArgs.model_fields["keychain_remote_ssh_semiautomatic_setup"].annotation
):
class KeychainCredentialSetupSSHConnectionSemiAutomaticSetup(KeychainCredentialRemoteSSHSemiautomaticSetup):
name: Excluded = excluded_field()
private_key: Excluded = excluded_field()


@single_argument_args("setup_ssh_connection")
class KeychainCredentialSetupSSHConnectionArgs(BaseModel):
class SetupSSHConnectionManualSetup(SSHCredentials):
private_key: Excluded = excluded_field()


class SetupSSHConnectionManual(BaseModel):
private_key: KeychainCredentialSetupSSHConnectionKeyNew | KeychainCredentialSetupSSHConnectionKeyExisting
connection_name: NonEmptyString
setup_type: Literal["SEMI-AUTOMATIC", "MANUAL"] = "MANUAL"
semi_automatic_setup: KeychainCredentialSetupSSHConnectionSemiAutomaticSetup | None = None
manual_setup: dict | None = None
setup_type: Literal["MANUAL"] = "MANUAL"
manual_setup: SSHCredentials


@single_argument_result
class KeychainCredentialSetupSSHConnectionResult(KeychainCredentialEntry):
pass
class SetupSSHConnectionSemiautomatic(SetupSSHConnectionManual):
setup_type: Literal["SEMI-AUTOMATIC"] = "SEMI-AUTOMATIC"
semi_automatic_setup: KeychainCredentialSetupSSHConnectionSemiAutomaticSetup
manual_setup: Excluded = excluded_field()


class KeychainCredentialSetupSSHConnectionArgs(BaseModel):
options: SetupSSHConnectionManual | SetupSSHConnectionSemiautomatic


class KeychainCredentialSetupSSHConnectionResult(BaseModel):
result: SSHCredentialsEntry
Loading

0 comments on commit 237a19c

Please sign in to comment.