Skip to content

Commit

Permalink
Black reformat
Browse files Browse the repository at this point in the history
  • Loading branch information
LDiazN committed Jan 22, 2025
1 parent 21a166c commit dcf48d5
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 116 deletions.
6 changes: 2 additions & 4 deletions ooniapi/services/ooniprobe/src/ooniprobe/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,5 @@ async def health(
@app.get("/")
async def root():
# TODO(art): fix this redirect by pointing health monitoring to /health
#return RedirectResponse("/docs")
return {
"msg": "hello from ooniprobe"
}
# return RedirectResponse("/docs")
return {"msg": "hello from ooniprobe"}
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,33 @@

log = logging.getLogger(__name__)


class ProbeLogin(BaseModel):
# Allow None username and password
# Allow None username and password
# to deliver informational 401 error when they're missing
username : str | None = None
username: str | None = None
# not actually used but necessary to be compliant with the old API schema
password : str | None = None
password: str | None = None


class ProbeLoginResponse(BaseModel):
token : str
expire : str
token: str
expire: str


@router.post("/login", tags=["ooniprobe"], response_model=ProbeLoginResponse)
def probe_login_post(
probe_login : ProbeLogin,
response : Response,
settings : Settings = Depends(get_settings),
probe_login: ProbeLogin,
response: Response,
settings: Settings = Depends(get_settings),
) -> ProbeLoginResponse:

if probe_login.username is None or probe_login.password is None:
raise HTTPException(status_code=401, detail="Missing credentials")

token = probe_login.username
# TODO: We have to find a way to explicitly log metrics with prometheus.
# We're currently using the instrumentator default metrics, like http response counts
# We're currently using the instrumentator default metrics, like http response counts
# Maybe using the same exporter as the instrumentator?
try:
dec = decode_jwt(token, audience="probe_login", key=settings.jwt_encryption_key)
Expand All @@ -48,11 +51,11 @@ def probe_login_post(
except jwt.exceptions.MissingRequiredClaimError:
log.info("probe login: invalid or missing claim")
# metrics.incr("probe_login_failed")
raise HTTPException(status_code=401, detail="Invalid credentials")
raise HTTPException(status_code=401, detail="Invalid credentials")
except jwt.exceptions.InvalidSignatureError:
log.info("probe login: invalid signature")
# metrics.incr("probe_login_failed")
raise HTTPException(status_code=401, detail="Invalid credentials")
raise HTTPException(status_code=401, detail="Invalid credentials")
except jwt.exceptions.DecodeError:
# Not a JWT token: treat it as a "legacy" login
# return jerror("Invalid or missing credentials", code=401)
Expand All @@ -65,38 +68,41 @@ def probe_login_post(
token = create_jwt(payload, key=settings.jwt_encryption_key)
# expiration string used by the probe e.g. 2006-01-02T15:04:05Z
expire = exp.strftime("%Y-%m-%dT%H:%M:%SZ")
login_response = ProbeLoginResponse(token=token, expire = expire)
login_response = ProbeLoginResponse(token=token, expire=expire)
setnocacheresponse(response)

return login_response


class ProbeRegister(BaseModel):
# None of this values is actually used, but I add them
# to keep it compliant with the old api
password : str
platform : str
probe_asn : str
probe_cc : str
software_name : str
software_version : str
supported_tests : List[str]
password: str
platform: str
probe_asn: str
probe_cc: str
software_name: str
software_version: str
supported_tests: List[str]


class ProbeRegisterResponse(BaseModel):
client_id: str
client_id: str


@router.post("/register", tags=["ooniprobe"], response_model=ProbeRegisterResponse)
def probe_register_post(
probe_register : ProbeRegister,
response : Response,
settings : Settings = Depends(get_settings),
probe_register: ProbeRegister,
response: Response,
settings: Settings = Depends(get_settings),
) -> ProbeRegisterResponse:
"""Probe Services: Register
Probes send a random string called password and receive a client_id
The client_id/password tuple is saved by the probe and long-lived
Note that most of the request body arguments are not actually
Note that most of the request body arguments are not actually
used but are kept here to use the same API as the old version
"""

Expand All @@ -112,13 +118,16 @@ def probe_register_post(

return register_response


class ProbeUpdate(BaseModel):
pass


class ProbeUpdateResponse(BaseModel):
status : str
status: str


@router.put("/update/{client_id}", tags=["ooniprobe"])
def probe_update_post(probe_update : ProbeUpdate) -> ProbeUpdateResponse:
def probe_update_post(probe_update: ProbeUpdate) -> ProbeUpdateResponse:
log.info("update successful")
return ProbeUpdateResponse(status="ok")
return ProbeUpdateResponse(status="ok")
2 changes: 1 addition & 1 deletion ooniapi/services/ooniprobe/src/ooniprobe/routers/v2/vpn.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,4 @@ def get_vpn_config(
# Pick 4 random endpoints to serve to the client
endpoints=random.sample(endpoints, min(len(endpoints), 4)),
date_updated=provider.date_updated.strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
)
)
49 changes: 31 additions & 18 deletions ooniapi/services/ooniprobe/src/ooniprobe/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Insert VPN credentials into database.
"""

import base64
from datetime import datetime, timezone
import itertools
Expand All @@ -27,11 +28,13 @@ class OpenVPNConfig(TypedDict):
cert: str
key: str


class OpenVPNEndpoint(TypedDict):
address: str
protocol: str
transport: str


def fetch_riseup_ca() -> str:
r = httpx.get(RISEUP_CA_URL)
r.raise_for_status()
Expand All @@ -50,6 +53,7 @@ def fetch_openvpn_config() -> OpenVPNConfig:
key, cert = pem.parse(pem_cert)
return OpenVPNConfig(ca=ca, cert=cert.as_text(), key=key.as_text())


def fetch_openvpn_endpoints() -> List[OpenVPNEndpoint]:
endpoints = []

Expand All @@ -59,38 +63,47 @@ def fetch_openvpn_endpoints() -> List[OpenVPNEndpoint]:
for ep in j["gateways"]:
ip = ep["ip_address"]
# TODO(art): do we want to store this metadata somewhere?
#location = ep["location"]
#hostname = ep["host"]
# location = ep["location"]
# hostname = ep["host"]
for t in ep["capabilities"]["transport"]:
if t["type"] != "openvpn":
continue
for transport, port in itertools.product(t["protocols"], t["ports"]):
endpoints.append(OpenVPNEndpoint(
address=f"{ip}:{port}",
protocol="openvpn",
transport=transport
))
endpoints.append(
OpenVPNEndpoint(
address=f"{ip}:{port}", protocol="openvpn", transport=transport
)
)
return endpoints


def format_endpoint(provider_name: str, ep: OONIProbeVPNProviderEndpoint) -> str:
return f"{ep.protocol}://{provider_name}.corp/?address={ep.address}&transport={ep.transport}"

def upsert_endpoints(db: Session, new_endpoints: List[OpenVPNEndpoint], provider: OONIProbeVPNProvider):
new_endpoints_map = {f'{ep["address"]}-{ep["protocol"]}-{ep["transport"]}': ep for ep in new_endpoints}

def upsert_endpoints(
db: Session, new_endpoints: List[OpenVPNEndpoint], provider: OONIProbeVPNProvider
):
new_endpoints_map = {
f'{ep["address"]}-{ep["protocol"]}-{ep["transport"]}': ep
for ep in new_endpoints
}
for endpoint in provider.endpoints:
key = f'{endpoint.address}-{endpoint.protocol}-{endpoint.transport}'
key = f"{endpoint.address}-{endpoint.protocol}-{endpoint.transport}"
if key in new_endpoints_map:
endpoint.date_updated = datetime.now(timezone.utc)
new_endpoints_map.pop(key)
else:
db.delete(endpoint)

for ep in new_endpoints_map.values():
db.add(OONIProbeVPNProviderEndpoint(
date_created=datetime.now(timezone.utc),
date_updated=datetime.now(timezone.utc),
protocol=ep["protocol"],
address=ep["address"],
transport=ep["transport"],
provider=provider
))
db.add(
OONIProbeVPNProviderEndpoint(
date_created=datetime.now(timezone.utc),
date_updated=datetime.now(timezone.utc),
protocol=ep["protocol"],
address=ep["address"],
transport=ep["transport"],
provider=provider,
)
)
6 changes: 5 additions & 1 deletion ooniapi/services/ooniprobe/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ def client_with_bad_settings():
client = TestClient(app)
yield client


JWT_ENCRYPTION_KEY = "super_secure"


@pytest.fixture
def client(alembic_migration):
app.dependency_overrides[get_settings] = make_override_get_settings(
Expand All @@ -79,6 +82,7 @@ def client(alembic_migration):
client = TestClient(app)
yield client


@pytest.fixture
def jwt_encryption_key():
return JWT_ENCRYPTION_KEY
return JWT_ENCRYPTION_KEY
86 changes: 44 additions & 42 deletions ooniapi/services/ooniprobe/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,47 @@
"openvpn://riseup.corp/?address=51.15.187.53:1194&transport=udp",
]


def test_create_providers(db, alembic_migration):
provider = OONIProbeVPNProvider(
provider_name="riseupvpn",
date_created=datetime.now(timezone.utc),
date_updated=datetime.now(timezone.utc),
openvpn_cert="OPENVPN_CERT",
openvpn_ca="OPENVPN_CA",
openvpn_key="OPENVPN_KEY"
openvpn_key="OPENVPN_KEY",
)
db.add(provider)
db.add(OONIProbeVPNProviderEndpoint(
date_created=datetime.now(timezone.utc)-timedelta(hours=1),
date_updated=datetime.now(timezone.utc)-timedelta(hours=1),
protocol="openvpn",
address="51.15.187.53:1194",
transport="tcp",
provider=provider
))
db.add(OONIProbeVPNProviderEndpoint(
date_created=datetime.now(timezone.utc)-timedelta(hours=1),
date_updated=datetime.now(timezone.utc)-timedelta(hours=1),
protocol="openvpn",
address="51.15.187.53:1194",
transport="udp",
provider=provider
))
db.add(OONIProbeVPNProviderEndpoint(
date_created=datetime.now(timezone.utc)-timedelta(hours=1),
date_updated=datetime.now(timezone.utc)-timedelta(hours=1),
protocol="openvpn",
address="1.1.1.1:1194",
transport="udp",
provider=provider
))
db.add(
OONIProbeVPNProviderEndpoint(
date_created=datetime.now(timezone.utc) - timedelta(hours=1),
date_updated=datetime.now(timezone.utc) - timedelta(hours=1),
protocol="openvpn",
address="51.15.187.53:1194",
transport="tcp",
provider=provider,
)
)
db.add(
OONIProbeVPNProviderEndpoint(
date_created=datetime.now(timezone.utc) - timedelta(hours=1),
date_updated=datetime.now(timezone.utc) - timedelta(hours=1),
protocol="openvpn",
address="51.15.187.53:1194",
transport="udp",
provider=provider,
)
)
db.add(
OONIProbeVPNProviderEndpoint(
date_created=datetime.now(timezone.utc) - timedelta(hours=1),
date_updated=datetime.now(timezone.utc) - timedelta(hours=1),
protocol="openvpn",
address="1.1.1.1:1194",
transport="udp",
provider=provider,
)
)
db.commit()

all_endpoints = db.query(OONIProbeVPNProviderEndpoint).all()
Expand All @@ -55,30 +62,25 @@ def test_create_providers(db, alembic_migration):
assert endpoint.provider.provider_name == "riseupvpn"
assert addresses == set(["51.15.187.53:1194", "1.1.1.1:1194"])

provider = db.query(OONIProbeVPNProvider).filter(
OONIProbeVPNProvider.provider_name == "riseupvpn",
OONIProbeVPNProvider.date_updated
> datetime.now(timezone.utc)
- timedelta(days=7),
).one()
provider = (
db.query(OONIProbeVPNProvider)
.filter(
OONIProbeVPNProvider.provider_name == "riseupvpn",
OONIProbeVPNProvider.date_updated
> datetime.now(timezone.utc) - timedelta(days=7),
)
.one()
)
assert len(provider.endpoints) == 3

new_endpoints = [
OpenVPNEndpoint(
address="51.15.187.53:1194",
protocol="openvpn",
transport="udp"
),
OpenVPNEndpoint(
address="51.15.187.53:1194",
protocol="openvpn",
transport="tcp"
address="51.15.187.53:1194", protocol="openvpn", transport="udp"
),
OpenVPNEndpoint(
address="3.2.1.3:1194",
protocol="openvpn",
transport="udp"
address="51.15.187.53:1194", protocol="openvpn", transport="tcp"
),
OpenVPNEndpoint(address="3.2.1.3:1194", protocol="openvpn", transport="udp"),
]

upsert_endpoints(db, new_endpoints, provider)
Expand Down
Loading

0 comments on commit dcf48d5

Please sign in to comment.