fix(lint): format SNMP and credential profile files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -41,12 +41,8 @@ def upgrade() -> None:
|
||||
""")
|
||||
)
|
||||
|
||||
conn.execute(
|
||||
sa.text("ALTER TABLE credential_profiles ENABLE ROW LEVEL SECURITY")
|
||||
)
|
||||
conn.execute(
|
||||
sa.text("ALTER TABLE credential_profiles FORCE ROW LEVEL SECURITY")
|
||||
)
|
||||
conn.execute(sa.text("ALTER TABLE credential_profiles ENABLE ROW LEVEL SECURITY"))
|
||||
conn.execute(sa.text("ALTER TABLE credential_profiles FORCE ROW LEVEL SECURITY"))
|
||||
|
||||
conn.execute(
|
||||
sa.text("""
|
||||
@@ -63,20 +59,13 @@ def upgrade() -> None:
|
||||
""")
|
||||
)
|
||||
|
||||
conn.execute(
|
||||
sa.text("GRANT SELECT ON credential_profiles TO poller_user")
|
||||
)
|
||||
conn.execute(
|
||||
sa.text("GRANT SELECT, INSERT, UPDATE, DELETE ON credential_profiles TO app_user")
|
||||
)
|
||||
conn.execute(sa.text("GRANT SELECT ON credential_profiles TO poller_user"))
|
||||
conn.execute(sa.text("GRANT SELECT, INSERT, UPDATE, DELETE ON credential_profiles TO app_user"))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
conn.execute(
|
||||
sa.text(
|
||||
"DROP POLICY IF EXISTS credential_profiles_tenant_isolation"
|
||||
" ON credential_profiles"
|
||||
)
|
||||
sa.text("DROP POLICY IF EXISTS credential_profiles_tenant_isolation ON credential_profiles")
|
||||
)
|
||||
op.drop_table("credential_profiles")
|
||||
|
||||
@@ -630,12 +630,8 @@ def upgrade() -> None:
|
||||
)
|
||||
|
||||
# -- RLS: system profiles visible to all tenants -----------------------
|
||||
conn.execute(
|
||||
sa.text("ALTER TABLE snmp_profiles ENABLE ROW LEVEL SECURITY")
|
||||
)
|
||||
conn.execute(
|
||||
sa.text("ALTER TABLE snmp_profiles FORCE ROW LEVEL SECURITY")
|
||||
)
|
||||
conn.execute(sa.text("ALTER TABLE snmp_profiles ENABLE ROW LEVEL SECURITY"))
|
||||
conn.execute(sa.text("ALTER TABLE snmp_profiles FORCE ROW LEVEL SECURITY"))
|
||||
conn.execute(
|
||||
sa.text("""
|
||||
CREATE POLICY snmp_profiles_tenant_isolation
|
||||
@@ -648,12 +644,8 @@ def upgrade() -> None:
|
||||
""")
|
||||
)
|
||||
|
||||
conn.execute(
|
||||
sa.text("GRANT SELECT ON snmp_profiles TO poller_user")
|
||||
)
|
||||
conn.execute(
|
||||
sa.text("GRANT SELECT, INSERT, UPDATE, DELETE ON snmp_profiles TO app_user")
|
||||
)
|
||||
conn.execute(sa.text("GRANT SELECT ON snmp_profiles TO poller_user"))
|
||||
conn.execute(sa.text("GRANT SELECT, INSERT, UPDATE, DELETE ON snmp_profiles TO app_user"))
|
||||
|
||||
# -- Seed 6 system profiles --------------------------------------------
|
||||
for profile in SEED_PROFILES:
|
||||
@@ -679,10 +671,5 @@ def upgrade() -> None:
|
||||
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
conn.execute(
|
||||
sa.text(
|
||||
"DROP POLICY IF EXISTS snmp_profiles_tenant_isolation"
|
||||
" ON snmp_profiles"
|
||||
)
|
||||
)
|
||||
conn.execute(sa.text("DROP POLICY IF EXISTS snmp_profiles_tenant_isolation ON snmp_profiles"))
|
||||
op.drop_table("snmp_profiles")
|
||||
|
||||
@@ -29,25 +29,12 @@ def upgrade() -> None:
|
||||
conn.execute(sa.text("SET lock_timeout = '3s'"))
|
||||
|
||||
conn.execute(
|
||||
sa.text(
|
||||
"ALTER TABLE devices"
|
||||
" ADD COLUMN device_type TEXT NOT NULL DEFAULT 'routeros'"
|
||||
)
|
||||
sa.text("ALTER TABLE devices ADD COLUMN device_type TEXT NOT NULL DEFAULT 'routeros'")
|
||||
)
|
||||
|
||||
conn.execute(
|
||||
sa.text(
|
||||
"ALTER TABLE devices"
|
||||
" ADD COLUMN snmp_port INTEGER DEFAULT 161"
|
||||
)
|
||||
)
|
||||
conn.execute(sa.text("ALTER TABLE devices ADD COLUMN snmp_port INTEGER DEFAULT 161"))
|
||||
|
||||
conn.execute(
|
||||
sa.text(
|
||||
"ALTER TABLE devices"
|
||||
" ADD COLUMN snmp_version TEXT"
|
||||
)
|
||||
)
|
||||
conn.execute(sa.text("ALTER TABLE devices ADD COLUMN snmp_version TEXT"))
|
||||
|
||||
conn.execute(
|
||||
sa.text(
|
||||
@@ -69,18 +56,8 @@ def upgrade() -> None:
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
|
||||
conn.execute(
|
||||
sa.text("ALTER TABLE devices DROP COLUMN IF EXISTS credential_profile_id")
|
||||
)
|
||||
conn.execute(
|
||||
sa.text("ALTER TABLE devices DROP COLUMN IF EXISTS snmp_profile_id")
|
||||
)
|
||||
conn.execute(
|
||||
sa.text("ALTER TABLE devices DROP COLUMN IF EXISTS snmp_version")
|
||||
)
|
||||
conn.execute(
|
||||
sa.text("ALTER TABLE devices DROP COLUMN IF EXISTS snmp_port")
|
||||
)
|
||||
conn.execute(
|
||||
sa.text("ALTER TABLE devices DROP COLUMN IF EXISTS device_type")
|
||||
)
|
||||
conn.execute(sa.text("ALTER TABLE devices DROP COLUMN IF EXISTS credential_profile_id"))
|
||||
conn.execute(sa.text("ALTER TABLE devices DROP COLUMN IF EXISTS snmp_profile_id"))
|
||||
conn.execute(sa.text("ALTER TABLE devices DROP COLUMN IF EXISTS snmp_version"))
|
||||
conn.execute(sa.text("ALTER TABLE devices DROP COLUMN IF EXISTS snmp_port"))
|
||||
conn.execute(sa.text("ALTER TABLE devices DROP COLUMN IF EXISTS device_type"))
|
||||
|
||||
@@ -44,11 +44,7 @@ def upgrade() -> None:
|
||||
|
||||
conn.execute(sa.text("SELECT create_hypertable('snmp_metrics', 'time')"))
|
||||
|
||||
conn.execute(
|
||||
sa.text(
|
||||
"SELECT add_retention_policy('snmp_metrics', INTERVAL '90 days')"
|
||||
)
|
||||
)
|
||||
conn.execute(sa.text("SELECT add_retention_policy('snmp_metrics', INTERVAL '90 days')"))
|
||||
|
||||
conn.execute(
|
||||
sa.text("""
|
||||
@@ -57,12 +53,8 @@ def upgrade() -> None:
|
||||
""")
|
||||
)
|
||||
|
||||
conn.execute(
|
||||
sa.text("ALTER TABLE snmp_metrics ENABLE ROW LEVEL SECURITY")
|
||||
)
|
||||
conn.execute(
|
||||
sa.text("ALTER TABLE snmp_metrics FORCE ROW LEVEL SECURITY")
|
||||
)
|
||||
conn.execute(sa.text("ALTER TABLE snmp_metrics ENABLE ROW LEVEL SECURITY"))
|
||||
conn.execute(sa.text("ALTER TABLE snmp_metrics FORCE ROW LEVEL SECURITY"))
|
||||
|
||||
conn.execute(
|
||||
sa.text("""
|
||||
@@ -75,9 +67,7 @@ def upgrade() -> None:
|
||||
""")
|
||||
)
|
||||
|
||||
conn.execute(
|
||||
sa.text("GRANT SELECT, INSERT ON snmp_metrics TO app_user")
|
||||
)
|
||||
conn.execute(sa.text("GRANT SELECT, INSERT ON snmp_metrics TO app_user"))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
|
||||
@@ -113,7 +113,10 @@ async def update_profile(
|
||||
"""Update a credential profile. Requires operator role or above."""
|
||||
await _check_tenant_access(current_user, tenant_id, db)
|
||||
return await credential_profile_service.update_profile(
|
||||
db=db, tenant_id=tenant_id, profile_id=profile_id, data=data,
|
||||
db=db,
|
||||
tenant_id=tenant_id,
|
||||
profile_id=profile_id,
|
||||
data=data,
|
||||
user_id=current_user.user_id,
|
||||
)
|
||||
|
||||
@@ -136,7 +139,9 @@ async def delete_profile(
|
||||
"""
|
||||
await _check_tenant_access(current_user, tenant_id, db)
|
||||
await credential_profile_service.delete_profile(
|
||||
db=db, tenant_id=tenant_id, profile_id=profile_id,
|
||||
db=db,
|
||||
tenant_id=tenant_id,
|
||||
profile_id=profile_id,
|
||||
user_id=current_user.user_id,
|
||||
)
|
||||
|
||||
|
||||
@@ -31,7 +31,11 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.config import settings
|
||||
from app.database import get_db
|
||||
from app.middleware.rbac import require_operator_or_above, require_scope, require_tenant_admin_or_above
|
||||
from app.middleware.rbac import (
|
||||
require_operator_or_above,
|
||||
require_scope,
|
||||
require_tenant_admin_or_above,
|
||||
)
|
||||
from app.middleware.tenant_context import CurrentUser, get_current_user
|
||||
from app.routers.devices import _check_tenant_access
|
||||
from app.schemas.snmp_profile import (
|
||||
@@ -252,7 +256,7 @@ async def update_profile(
|
||||
|
||||
sql = f"""
|
||||
UPDATE snmp_profiles
|
||||
SET {', '.join(set_clauses)}
|
||||
SET {", ".join(set_clauses)}
|
||||
WHERE id = :profile_id AND tenant_id = :tenant_id
|
||||
RETURNING id, tenant_id, name, description, sys_object_id, vendor,
|
||||
category, is_system, created_at, updated_at
|
||||
|
||||
@@ -46,9 +46,7 @@ class CredentialProfileCreate(BaseModel):
|
||||
@classmethod
|
||||
def validate_credential_type(cls, v: str) -> str:
|
||||
if v not in VALID_CREDENTIAL_TYPES:
|
||||
raise ValueError(
|
||||
f"credential_type must be one of: {', '.join(VALID_CREDENTIAL_TYPES)}"
|
||||
)
|
||||
raise ValueError(f"credential_type must be one of: {', '.join(VALID_CREDENTIAL_TYPES)}")
|
||||
return v
|
||||
|
||||
@model_validator(mode="after")
|
||||
@@ -141,9 +139,7 @@ class CredentialProfileUpdate(BaseModel):
|
||||
if v is None:
|
||||
return v
|
||||
if v not in VALID_CREDENTIAL_TYPES:
|
||||
raise ValueError(
|
||||
f"credential_type must be one of: {', '.join(VALID_CREDENTIAL_TYPES)}"
|
||||
)
|
||||
raise ValueError(f"credential_type must be one of: {', '.join(VALID_CREDENTIAL_TYPES)}")
|
||||
return v
|
||||
|
||||
@model_validator(mode="after")
|
||||
@@ -151,9 +147,14 @@ class CredentialProfileUpdate(BaseModel):
|
||||
"""Validate credential fields only when credential_type or credential fields change."""
|
||||
# Collect which credential fields were provided
|
||||
cred_fields = {
|
||||
"username", "password", "community",
|
||||
"security_level", "auth_protocol", "auth_passphrase",
|
||||
"priv_protocol", "priv_passphrase",
|
||||
"username",
|
||||
"password",
|
||||
"community",
|
||||
"security_level",
|
||||
"auth_protocol",
|
||||
"auth_passphrase",
|
||||
"priv_protocol",
|
||||
"priv_passphrase",
|
||||
}
|
||||
has_cred_changes = any(getattr(self, f) is not None for f in cred_fields)
|
||||
|
||||
|
||||
@@ -65,7 +65,9 @@ def _build_credential_json(data: CredentialProfileCreate | CredentialProfileUpda
|
||||
raise ValueError(f"Unknown credential_type: {ct}")
|
||||
|
||||
|
||||
def _profile_response(profile: CredentialProfile, device_count: int = 0) -> CredentialProfileResponse:
|
||||
def _profile_response(
|
||||
profile: CredentialProfile, device_count: int = 0
|
||||
) -> CredentialProfileResponse:
|
||||
"""Build a CredentialProfileResponse from an ORM instance."""
|
||||
return CredentialProfileResponse(
|
||||
id=profile.id,
|
||||
@@ -116,9 +118,11 @@ async def get_profiles(
|
||||
credential_type: str | None = None,
|
||||
) -> CredentialProfileListResponse:
|
||||
"""List all credential profiles for a tenant."""
|
||||
query = select(CredentialProfile).where(
|
||||
CredentialProfile.tenant_id == tenant_id
|
||||
).order_by(CredentialProfile.name)
|
||||
query = (
|
||||
select(CredentialProfile)
|
||||
.where(CredentialProfile.tenant_id == tenant_id)
|
||||
.order_by(CredentialProfile.name)
|
||||
)
|
||||
|
||||
if credential_type:
|
||||
query = query.where(CredentialProfile.credential_type == credential_type)
|
||||
@@ -141,10 +145,7 @@ async def get_profiles(
|
||||
for row in count_result:
|
||||
device_counts[row.credential_profile_id] = row.cnt
|
||||
|
||||
responses = [
|
||||
_profile_response(p, device_count=device_counts.get(p.id, 0))
|
||||
for p in profiles
|
||||
]
|
||||
responses = [_profile_response(p, device_count=device_counts.get(p.id, 0)) for p in profiles]
|
||||
return CredentialProfileListResponse(profiles=responses)
|
||||
|
||||
|
||||
@@ -211,9 +212,14 @@ async def update_profile(
|
||||
|
||||
# Determine if credential re-encryption is needed
|
||||
cred_fields = {
|
||||
"username", "password", "community",
|
||||
"security_level", "auth_protocol", "auth_passphrase",
|
||||
"priv_protocol", "priv_passphrase",
|
||||
"username",
|
||||
"password",
|
||||
"community",
|
||||
"security_level",
|
||||
"auth_protocol",
|
||||
"auth_passphrase",
|
||||
"priv_protocol",
|
||||
"priv_passphrase",
|
||||
}
|
||||
has_cred_changes = any(getattr(data, f) is not None for f in cred_fields)
|
||||
type_changed = data.credential_type is not None
|
||||
@@ -241,13 +247,18 @@ async def update_profile(
|
||||
action="credential_profile.update",
|
||||
resource_type="credential_profile",
|
||||
resource_id=str(profile.id),
|
||||
details={"name": profile.name, "updated_fields": list(data.model_dump(exclude_unset=True).keys())},
|
||||
details={
|
||||
"name": profile.name,
|
||||
"updated_fields": list(data.model_dump(exclude_unset=True).keys()),
|
||||
},
|
||||
)
|
||||
|
||||
return _profile_response(profile, device_count=dc)
|
||||
|
||||
|
||||
def _merge_update(data: CredentialProfileUpdate, profile: CredentialProfile) -> CredentialProfileUpdate:
|
||||
def _merge_update(
|
||||
data: CredentialProfileUpdate, profile: CredentialProfile
|
||||
) -> CredentialProfileUpdate:
|
||||
"""For partial credential updates, overlay data onto existing profile type.
|
||||
|
||||
When credential_type is not changing but individual credential fields are,
|
||||
|
||||
Reference in New Issue
Block a user