feat(16-02): extend Device struct and queries for SNMP and credential profiles
- Add 7 new fields to store.Device: DeviceType, SNMPPort, SNMPVersion, SNMPProfileID, CredentialProfileID, ProfileEncryptedCredentials, ProfileEncryptedCredentialsTransit - Update FetchDevices query with LEFT JOIN credential_profiles and expanded WHERE clause (credential_profile_id IS NOT NULL) - Update GetDevice query with same JOIN and new columns - COALESCE defaults: device_type='routeros', snmp_port=161 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,19 @@ type Device struct {
|
|||||||
CACertPEM *string // PEM-encoded CA cert (only populated when TLSMode = "portal_ca")
|
CACertPEM *string // PEM-encoded CA cert (only populated when TLSMode = "portal_ca")
|
||||||
SSHPort int // SSH port for config backup (default 22)
|
SSHPort int // SSH port for config backup (default 22)
|
||||||
SSHHostKeyFingerprint *string // TOFU SSH host key fingerprint (SHA256:base64)
|
SSHHostKeyFingerprint *string // TOFU SSH host key fingerprint (SHA256:base64)
|
||||||
|
|
||||||
|
// Protocol discrimination
|
||||||
|
DeviceType string // "routeros" (default) or "snmp"
|
||||||
|
|
||||||
|
// SNMP-specific fields (only populated when DeviceType = "snmp")
|
||||||
|
SNMPPort int // default 161
|
||||||
|
SNMPVersion *string // "v1", "v2c", "v3"
|
||||||
|
SNMPProfileID *string // UUID -> snmp_profiles table
|
||||||
|
|
||||||
|
// Credential profile (applies to both device types)
|
||||||
|
CredentialProfileID *string // UUID -> credential_profiles table
|
||||||
|
ProfileEncryptedCredentials []byte // from credential_profiles (fallback)
|
||||||
|
ProfileEncryptedCredentialsTransit *string // from credential_profiles (fallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeviceStore manages PostgreSQL connections for device data access.
|
// DeviceStore manages PostgreSQL connections for device data access.
|
||||||
@@ -69,13 +82,23 @@ func (s *DeviceStore) FetchDevices(ctx context.Context) ([]Device, error) {
|
|||||||
d.tls_mode,
|
d.tls_mode,
|
||||||
ca.cert_pem,
|
ca.cert_pem,
|
||||||
COALESCE(d.ssh_port, 22),
|
COALESCE(d.ssh_port, 22),
|
||||||
d.ssh_host_key_fingerprint
|
d.ssh_host_key_fingerprint,
|
||||||
|
COALESCE(d.device_type, 'routeros'),
|
||||||
|
COALESCE(d.snmp_port, 161),
|
||||||
|
d.snmp_version,
|
||||||
|
d.snmp_profile_id::text,
|
||||||
|
d.credential_profile_id::text,
|
||||||
|
cp.encrypted_credentials,
|
||||||
|
cp.encrypted_credentials_transit
|
||||||
FROM devices d
|
FROM devices d
|
||||||
LEFT JOIN certificate_authorities ca
|
LEFT JOIN certificate_authorities ca
|
||||||
ON d.tenant_id = ca.tenant_id
|
ON d.tenant_id = ca.tenant_id
|
||||||
AND d.tls_mode = 'portal_ca'
|
AND d.tls_mode = 'portal_ca'
|
||||||
|
LEFT JOIN credential_profiles cp
|
||||||
|
ON d.credential_profile_id = cp.id
|
||||||
WHERE d.encrypted_credentials IS NOT NULL
|
WHERE d.encrypted_credentials IS NOT NULL
|
||||||
OR d.encrypted_credentials_transit IS NOT NULL
|
OR d.encrypted_credentials_transit IS NOT NULL
|
||||||
|
OR d.credential_profile_id IS NOT NULL
|
||||||
`
|
`
|
||||||
|
|
||||||
rows, err := s.pool.Query(ctx, query)
|
rows, err := s.pool.Query(ctx, query)
|
||||||
@@ -101,6 +124,13 @@ func (s *DeviceStore) FetchDevices(ctx context.Context) ([]Device, error) {
|
|||||||
&d.CACertPEM,
|
&d.CACertPEM,
|
||||||
&d.SSHPort,
|
&d.SSHPort,
|
||||||
&d.SSHHostKeyFingerprint,
|
&d.SSHHostKeyFingerprint,
|
||||||
|
&d.DeviceType,
|
||||||
|
&d.SNMPPort,
|
||||||
|
&d.SNMPVersion,
|
||||||
|
&d.SNMPProfileID,
|
||||||
|
&d.CredentialProfileID,
|
||||||
|
&d.ProfileEncryptedCredentials,
|
||||||
|
&d.ProfileEncryptedCredentialsTransit,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, fmt.Errorf("scanning device row: %w", err)
|
return nil, fmt.Errorf("scanning device row: %w", err)
|
||||||
}
|
}
|
||||||
@@ -130,11 +160,20 @@ func (s *DeviceStore) GetDevice(ctx context.Context, deviceID string) (Device, e
|
|||||||
d.tls_mode,
|
d.tls_mode,
|
||||||
ca.cert_pem,
|
ca.cert_pem,
|
||||||
COALESCE(d.ssh_port, 22),
|
COALESCE(d.ssh_port, 22),
|
||||||
d.ssh_host_key_fingerprint
|
d.ssh_host_key_fingerprint,
|
||||||
|
COALESCE(d.device_type, 'routeros'),
|
||||||
|
COALESCE(d.snmp_port, 161),
|
||||||
|
d.snmp_version,
|
||||||
|
d.snmp_profile_id::text,
|
||||||
|
d.credential_profile_id::text,
|
||||||
|
cp.encrypted_credentials,
|
||||||
|
cp.encrypted_credentials_transit
|
||||||
FROM devices d
|
FROM devices d
|
||||||
LEFT JOIN certificate_authorities ca
|
LEFT JOIN certificate_authorities ca
|
||||||
ON d.tenant_id = ca.tenant_id
|
ON d.tenant_id = ca.tenant_id
|
||||||
AND d.tls_mode = 'portal_ca'
|
AND d.tls_mode = 'portal_ca'
|
||||||
|
LEFT JOIN credential_profiles cp
|
||||||
|
ON d.credential_profile_id = cp.id
|
||||||
WHERE d.id = $1
|
WHERE d.id = $1
|
||||||
`
|
`
|
||||||
var d Device
|
var d Device
|
||||||
@@ -152,6 +191,13 @@ func (s *DeviceStore) GetDevice(ctx context.Context, deviceID string) (Device, e
|
|||||||
&d.CACertPEM,
|
&d.CACertPEM,
|
||||||
&d.SSHPort,
|
&d.SSHPort,
|
||||||
&d.SSHHostKeyFingerprint,
|
&d.SSHHostKeyFingerprint,
|
||||||
|
&d.DeviceType,
|
||||||
|
&d.SNMPPort,
|
||||||
|
&d.SNMPVersion,
|
||||||
|
&d.SNMPProfileID,
|
||||||
|
&d.CredentialProfileID,
|
||||||
|
&d.ProfileEncryptedCredentials,
|
||||||
|
&d.ProfileEncryptedCredentialsTransit,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Device{}, fmt.Errorf("querying device %s: %w", deviceID, err)
|
return Device{}, fmt.Errorf("querying device %s: %w", deviceID, err)
|
||||||
|
|||||||
Reference in New Issue
Block a user