Skip to content

Commit 3a38839

Browse files
authored
Merge pull request #2 from TaskarCenterAtUW/security-fixes
Small security flags and linter feedback
2 parents e9f7e93 + 4961e4b commit 3a38839

12 files changed

Lines changed: 59 additions & 39 deletions

File tree

File renamed without changes.

alembic_osm/env.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
# Add the project root directory to the Python path
99
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
1010

11+
from alembic import context
1112
from sqlalchemy import pool
1213
from sqlalchemy.engine import Connection
1314
from sqlalchemy.ext.asyncio import async_engine_from_config
1415

15-
from alembic import context
1616
from api.core.config import settings
1717
from api.core.database import Base
1818

alembic_osm/versions/9221408912dd_add_user_role_table.py

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,68 +5,84 @@
55
Create Date: 2026-01-29 14:54:10.669000
66
77
"""
8+
89
from typing import Sequence, Union
910

11+
import sqlalchemy as sa
1012
from alembic import op
1113
from sqlalchemy import inspect, text
12-
import sqlalchemy as sa
13-
1414

1515
# revision identifiers, used by Alembic.
16-
revision: str = '9221408912dd'
16+
revision: str = "9221408912dd"
1717
down_revision: Union[str, None] = None
1818
branch_labels: Union[str, Sequence[str], None] = None
1919
depends_on: Union[str, Sequence[str], None] = None
2020

2121

2222
def upgrade() -> None:
2323
bind = op.get_bind()
24+
assert bind is not None
2425
insp = inspect(bind)
2526

2627
# Add unique constraint on users.auth_uid (if not already present)
2728
constraint_exists = bind.execute(
2829
text("SELECT 1 FROM pg_constraint WHERE conname = 'auth_uid_unique'")
2930
).scalar()
3031
if not constraint_exists:
31-
op.create_unique_constraint('auth_uid_unique', 'users', ['auth_uid'])
32+
op.create_unique_constraint("auth_uid_unique", "users", ["auth_uid"])
3233

3334
# Create the workspace_role enum type (if not already present)
3435
result = bind.execute(
3536
text("SELECT 1 FROM pg_type WHERE typname = 'workspace_role'")
3637
)
3738
if not result.scalar():
38-
workspace_role = sa.Enum('lead', 'validator', 'contributor', name='workspace_role')
39+
workspace_role = sa.Enum(
40+
"lead", "validator", "contributor", name="workspace_role"
41+
)
3942
workspace_role.create(bind)
4043

4144
# Create the user_workspace_roles table (if not already present)
42-
if not insp.has_table('user_workspace_roles'):
45+
if not insp.has_table("user_workspace_roles"):
4346
op.create_table(
44-
'user_workspace_roles',
45-
sa.Column('user_auth_uid', sa.Uuid(), nullable=False),
46-
sa.Column('workspace_id', sa.BigInteger(), nullable=False),
47-
sa.Column('role', sa.Enum('lead', 'validator', 'contributor', name='workspace_role', create_type=False), nullable=False),
48-
sa.ForeignKeyConstraint(['user_auth_uid'], ['users.auth_uid']),
49-
sa.PrimaryKeyConstraint('user_auth_uid', 'workspace_id')
47+
"user_workspace_roles",
48+
sa.Column("user_auth_uid", sa.Uuid(), nullable=False),
49+
sa.Column("workspace_id", sa.BigInteger(), nullable=False),
50+
sa.Column(
51+
"role",
52+
sa.Enum(
53+
"lead",
54+
"validator",
55+
"contributor",
56+
name="workspace_role",
57+
create_type=False,
58+
),
59+
nullable=False,
60+
),
61+
sa.ForeignKeyConstraint(["user_auth_uid"], ["users.auth_uid"]),
62+
sa.PrimaryKeyConstraint("user_auth_uid", "workspace_id"),
5063
)
5164

5265

5366
def downgrade() -> None:
5467
bind = op.get_bind()
68+
assert bind is not None
5569
insp = inspect(bind)
5670

57-
if insp.has_table('user_workspace_roles'):
58-
op.drop_table('user_workspace_roles')
71+
if insp.has_table("user_workspace_roles"):
72+
op.drop_table("user_workspace_roles")
5973

6074
# Drop the enum type
6175
result = bind.execute(
6276
text("SELECT 1 FROM pg_type WHERE typname = 'workspace_role'")
6377
)
6478
if result.scalar():
65-
workspace_role = sa.Enum('lead', 'validator', 'contributor', name='workspace_role')
79+
workspace_role = sa.Enum(
80+
"lead", "validator", "contributor", name="workspace_role"
81+
)
6682
workspace_role.drop(bind)
6783

6884
constraint_exists = bind.execute(
6985
text("SELECT 1 FROM pg_constraint WHERE conname = 'auth_uid_unique'")
7086
).scalar()
7187
if constraint_exists:
72-
op.drop_constraint('auth_uid_unique', 'users', type_='unique')
88+
op.drop_constraint("auth_uid_unique", "users", type_="unique")

alembic_task/env.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
# Add the project root directory to the Python path
99
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
1010

11+
from alembic import context
1112
from sqlalchemy import pool
1213
from sqlalchemy.engine import Connection
1314
from sqlalchemy.ext.asyncio import async_engine_from_config
1415

15-
from alembic import context
1616
from api.core.config import settings
1717
from api.core.database import Base
1818

alembic_task/versions/add6266277c7_.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
"""
88

99
import sqlalchemy as sa
10-
1110
from alembic import op
1211

1312
# revision identifiers, used by Alembic.

api/core/config.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ class Settings(BaseSettings):
1212
#
1313
CORS_ORIGINS: list[str] = []
1414

15-
TASK_DATABASE_URL: str = "postgresql+asyncpg://user:pass@localhost:5432/tasking_manager"
16-
OSM_DATABASE_URL: str = "postgresql+asyncpg://user:pass@localhost:5432/tasking_manager"
15+
TASK_DATABASE_URL: str = (
16+
"postgresql+asyncpg://user:pass@localhost:5432/tasking_manager"
17+
)
18+
OSM_DATABASE_URL: str = (
19+
"postgresql+asyncpg://user:pass@localhost:5432/tasking_manager"
20+
)
1721

1822
TDEI_BACKEND_URL: str = "https://portal-api-dev.tdei.us/api/v1/"
1923
TDEI_OIDC_URL: str = "https://account-dev.tdei.us/"
@@ -28,7 +32,6 @@ class Settings(BaseSettings):
2832

2933
# proxy destination--"osm-web" is a virtual docker network endpoint
3034
WS_OSM_HOST: str = "http://osm-web"
31-
#WS_OSM_HOST: str = "https://osm.workspaces-dev.sidewalks.washington.edu"
3235

3336
SENTRY_DSN: str = ""
3437

@@ -37,4 +40,5 @@ class Settings(BaseSettings):
3740
env_file_encoding="utf-8",
3841
)
3942

43+
4044
settings = Settings()

api/main.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import re
3+
import sys
34
from contextlib import asynccontextmanager
45

56
import httpx
@@ -36,9 +37,6 @@
3637
# Set up logging configuration
3738
setup_logging()
3839

39-
# Optional: Run migrations on startup
40-
run_migrations()
41-
4240
# Set up logger for this module
4341
logger = get_logger(__name__)
4442

@@ -48,6 +46,10 @@
4846

4947
@asynccontextmanager
5048
async def lifespan(_app: FastAPI):
49+
# only run migrations when not under test
50+
if "pytest" not in sys.modules:
51+
run_migrations()
52+
5153
# Run before app bootstrap:
5254
global _osm_client
5355
_osm_client = httpx.AsyncClient(

api/src/teams/repository.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ async def get_all(self, workspace_id: int) -> list[WorkspaceTeamItem]:
2424
.where(WorkspaceTeam.workspace_id == workspace_id)
2525
)
2626

27-
return [WorkspaceTeamItem.from_team(x) for x in result.scalars()]
27+
return [WorkspaceTeamItem.from_team(x) for x in result.scalars().all()]
2828

2929
async def get(self, id: int, load_members: bool = False) -> WorkspaceTeam:
3030
query = select(WorkspaceTeam).where(WorkspaceTeam.id == id)
@@ -59,14 +59,13 @@ async def assert_team_in_workspace(self, id: int, workspace_id: int):
5959
raise NotFoundException(f"Team {id} not in workspace {workspace_id}")
6060

6161
async def create(self, workspace_id: int, data: WorkspaceTeamCreate) -> int:
62-
team = WorkspaceTeam()
63-
team.workspace_id = workspace_id
64-
team.name = data.name
62+
team = WorkspaceTeam(name=data.name, workspace_id=workspace_id)
6563

6664
self.session.add(team)
6765
await self.session.commit()
6866
await self.session.refresh(team)
6967

68+
assert team.id is not None
7069
return team.id
7170

7271
async def update(self, id: int, data: WorkspaceTeamUpdate):
@@ -77,7 +76,7 @@ async def update(self, id: int, data: WorkspaceTeamUpdate):
7776
await self.session.commit()
7877

7978
async def delete(self, id: int) -> None:
80-
await self.session.exec(delete(WorkspaceTeam).where(WorkspaceTeam.id == id))
79+
await self.session.execute(delete(WorkspaceTeam).where(WorkspaceTeam.id == id)) # type: ignore[arg-type]
8180
await self.session.commit()
8281

8382
async def get_members(self, id: int) -> list[User]:

api/src/teams/routes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from fastapi import APIRouter, Depends, HTTPException, status
1+
from fastapi import APIRouter, Depends, status
22
from sqlmodel.ext.asyncio.session import AsyncSession
33

44
from api.core.database import get_osm_session, get_task_session

api/src/teams/schemas.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Self, TYPE_CHECKING
1+
from typing import TYPE_CHECKING, Self
22

33
from sqlmodel import Field, Relationship, SQLModel
44

0 commit comments

Comments
 (0)