Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ ios/**/.DS_Store
ios/**/*.xcuserstate
ios/**/xcuserdata/
ios/OoniAuthBindings/OoniAuthFFI.xcframework/
ooniauth-py/wheels/
18 changes: 14 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion ooniauth-py/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ edition = "2021"
name = "ooniauth_py"
crate-type = ["cdylib", "rlib"]

[features]
default = []
extension-module = ["pyo3/extension-module"]

[dependencies]
cmz = { workspace = true }
ooniauth-core = {path = "../ooniauth-core"}
pyo3 = {version = "0.26.0", features = ["extension-module", "abi3-py310"]}
pyo3 = {version = "0.26.0", features = ["abi3-py310"]}
pyo3-stub-gen = "0.14.1"
rand = {workspace = true}
bincode = {workspace = true}
serde = {workspace = true}
thiserror = {workspace = true}
base64 = "0.22.1"

[build-dependencies]
pyo3-build-config = "0.28.3"
39 changes: 7 additions & 32 deletions ooniauth-py/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,41 +25,16 @@ the resulting Python package during development, see more details

### Testing installation

If you try to run the tests as you usually would with `cargo test`, you will get linking errors. This
happens because Maturin provides a build configuration with all the linking flags required to build
the library. However, it does not provide a `maturing test` command that could help you with this.

A possible solution is to manually specify the linking flags to the compiler, but in order to do this
you will probably need to download the specific Python version (3.10). A good way to do this
is using [Pyenv](https://github.com/pyenv/pyenv):

1. [Install Pyenv](https://github.com/pyenv/pyenv?tab=readme-ov-file#installation)
2. Install Python 3.10.0 with pyenv: `pyenv install 3.10.0`

With the Python version installed, you can create a `.cargo/config.toml`
with the linking flags. Create the file in `userauth/.cargo/config.toml`
and fill the following template:

```toml
[target.'cfg(all())']
rustflags = [
"-C", "link-arg=-Wl,-rpath,<YOUR PYENV PATH HERE>/.pyenv/versions/3.10.0/lib",
"-C", "link-arg=-L<YOUR PYENV PATH HERE>/.pyenv/versions/3.10.0/lib",
"-C", "link-arg=-lpython3.10",
]
```
Run the Rust tests with:

Example result:
```toml
[target.'cfg(all())']
rustflags = [
"-C", "link-arg=-Wl,-rpath,/home/ooni/.pyenv/versions/3.10.0/lib",
"-C", "link-arg=-L/home/ooni/.pyenv/versions/3.10.0/lib",
"-C", "link-arg=-lpython3.10",
]
```bash
cargo test
```

**Note**: Make sure to create this file in `userauth/.cargo/config.toml` and not in `userauth/ooniauth-py/.cargo.toml`
The Python extension-module linker mode is enabled only for Maturin builds.
If you need to test against a specific Python interpreter, activate a virtualenv
or set `PYO3_PYTHON` before running the tests. The virtualenv must point to a
base Python installation which provides an embeddable shared library.

### Usage

Expand Down
48 changes: 7 additions & 41 deletions ooniauth-py/build.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,13 @@
// Build helper for plain Cargo workflows.
// Maturin usually sets Python link flags; this script queries `python3-config`
// and emits equivalent linker directives so `cargo build/test` can link PyO3.
// This is needed because Cargo does not automatically inherit Maturin's linker setup.
use std::process::Command;

fn main() {
pyo3_build_config::add_extension_module_link_args();

println!("cargo:rerun-if-env-changed=PYO3_PYTHON");
println!("cargo:rerun-if-env-changed=PYTHON_SYS_EXECUTABLE");

let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
if target_os != "macos" {
return;
}

let flags = python3_config_ldflags();
if let Some(flags) = flags {
emit_link_flags(&flags);
}
}

fn python3_config_ldflags() -> Option<String> {
run_python3_config(&["--embed", "--ldflags"]).or_else(|| run_python3_config(&["--ldflags"]))
}

fn run_python3_config(args: &[&str]) -> Option<String> {
let output = Command::new("python3-config").args(args).output().ok()?;
if !output.status.success() {
return None;
}
String::from_utf8(output.stdout).ok()
}

fn emit_link_flags(flags: &str) {
let mut iter = flags.split_whitespace().peekable();
while let Some(flag) = iter.next() {
if let Some(path) = flag.strip_prefix("-L") {
println!("cargo:rustc-link-search=native={path}");
} else if let Some(lib) = flag.strip_prefix("-l") {
println!("cargo:rustc-link-lib={lib}");
} else if flag == "-framework" {
if let Some(name) = iter.next() {
println!("cargo:rustc-link-lib=framework={name}");
}
}
}
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-env-changed=CC");
println!("cargo:rerun-if-env-changed=CFLAGS");
println!("cargo:rerun-if-env-changed=LDFLAGS");
println!("cargo:rerun-if-env-changed=RUSTFLAGS");
}
51 changes: 14 additions & 37 deletions ooniauth-py/ooniauth_py.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,40 @@ import builtins
import typing

__version__: builtins.str

class CredentialError(builtins.Exception):
r"""
An authentication error
"""

...

class DeserializationFailed(builtins.Exception):
r"""
An error trying to deserialize a base64-encoded payload
"""

...

class ProtocolError(builtins.Exception):
r"""
An error performing the protocol
"""

...

class ServerState:
def __new__(cls) -> ServerState: ...
@staticmethod
def from_creds(public_parameters: str, secret_key: str) -> ServerState:
def from_creds(public_parameters:str, secret_key:str) -> ServerState:
r"""
Create a new server state from base64-encoded keys
This is meant to be used by the server, so it can store the keys somewhere and recreate the
state when needed
"""

def get_secret_key(self) -> str: ...
def get_public_parameters(self) -> str: ...
def handle_registration_request(self, registration_request: str) -> str: ...
def handle_registration_request(self, registration_request:str) -> str: ...
@staticmethod
def today() -> builtins.int: ...
def handle_submit_request(
self,
nym: str,
request: str,
probe_cc: str,
probe_asn: str,
age_range: tuple[builtins.int, builtins.int],
min_measurement_count: builtins.int,
) -> str: ...
def handle_update_request(
self, req: str, old_public_params: str, old_secret_key: str
) -> str: ...
def handle_submit_request(self, nym:str, request:str, probe_cc:str, probe_asn:str, age_range:tuple[builtins.int, builtins.int], min_measurement_count:builtins.int) -> str: ...
def handle_update_request(self, req:str, old_public_params:str, old_secret_key:str) -> str: ...

class SubmitRequest:
@property
Expand All @@ -62,46 +47,38 @@ class SubmitRequest:
def request(self) -> str: ...

class UserState:
def __new__(cls, public_params: str) -> UserState: ...
def __new__(cls, public_params:str) -> UserState: ...
def get_credential(self) -> typing.Optional[str]: ...
def set_public_params(self, new_public_params: str) -> None: ...
def set_public_params(self, new_public_params:str) -> None: ...
def make_registration_request(self) -> str: ...
def handle_registration_response(self, resp: str) -> None:
def handle_registration_response(self, resp:str) -> None:
r"""
Handle a registration response sent by the server, updating your credentials

Note that this function will only work if you previously called
`make_registration_request`
"""

def make_submit_request(
self,
probe_cc: str,
probe_asn: str,
age_range: tuple[builtins.int, builtins.int],
min_measurement_count: builtins.int,
) -> SubmitRequest: ...
def handle_submit_response(self, response: str) -> None:
def make_submit_request(self, probe_cc:str, probe_asn:str, age_range:tuple[builtins.int, builtins.int], min_measurement_count:builtins.int) -> SubmitRequest: ...
def handle_submit_response(self, response:str) -> None:
r"""
Handle a submit response sent by the server, updating your credentials

Note that this function will only work if you previously called
`make_submit_request`
"""

def make_credential_update_request(self) -> str:
r"""
Creates a credential update request to be sent to the server.
"""

def handle_credential_update_response(self, resp: str) -> None:
def handle_credential_update_response(self, resp:str) -> None:
r"""
Handles the credential update response sent by the server, updating your credentials.

This function only works if you previosly called `make_credential_update_request`
"""

def get_protocol_version() -> builtins.str:
r"""
Returns the version of the `ooniauth-core`, the actual protocol implementation.
"""

2 changes: 1 addition & 1 deletion ooniauth-py/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ Homepage = "https://github.com/ooni/userauth"
Issues = "https://github.com/ooni/userauth/issues"

[tool.maturin]
features = ["pyo3/extension-module"]
features = ["extension-module"]
Loading