Metadata-Version: 2.4
Name: atabet-token
Version: 1.1.0
Summary: Token validation utilities for Atabet systems
Author: Joseph Chen
Author-email: Joseph Chen <josephchenhk@gmail.com>
Maintainer: Joseph Chen
Maintainer-email: Joseph Chen <josephchenhk@gmail.com>
License: Proprietary
Keywords: token,validation,security
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: pytz
Requires-Dist: requests
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Dynamic: author
Dynamic: maintainer

# atabet-token

Token validation utilities packaged with Cython.

## Installation

### Prerequisites

1. **Conda environment** (recommended):
   ```bash
   conda env create -f environment.yml
   conda activate atabet_token_env
   ```

2. **C++ Compiler** (required for Windows):
   - **MinGW (MSYS2)** - Recommended if you have MSYS2 installed
   - **Microsoft C++ Build Tools** - Alternative option

### Quick Start

#### Using MinGW (if you have MSYS2):

```bash
conda activate atabet_token_env

# Configure MinGW (one-time setup)
python configure_mingw.py

# Build and install
python setup.py build_ext --inplace --compiler=mingw32
pip install -e .[dev]
```

Or use the automated script:
```bash
build_with_mingw.bat
```

#### Using MSVC (default):

```bash
conda activate atabet_token_env
pip install -e .[dev]
```

### Run Tests

```bash
pytest
```

## GitHub Actions: wheels and PyPI

Workflow: [`.github/workflows/build-wheels.yml`](.github/workflows/build-wheels.yml). Wheels are built with [cibuildwheel](https://cibuildwheel.readthedocs.io/); options live in [`pyproject.toml`](pyproject.toml) under `[tool.cibuildwheel]`.

**When jobs run (push):** the Linux / Windows / macOS matrix runs only if the **head commit message** includes one of:

| Tag | Effect |
|-----|--------|
| `[build ci]` (or `[Build CI]`, `[BUILD CI]`) | Build wheels and upload per-OS artifacts; no publish. |
| `[pub pypi]` (or `[Pub PyPI]`, `[PUB PYPI]`) | Build wheels, then publish merged wheels to [PyPI](https://pypi.org/) (production). |
| `[pub test.pypi]` (or `[Pub test.pypi]`, `[PUB TEST.PYPI]`) | Build wheels, then publish to [TestPyPI](https://test.pypi.org/). |

Plain pushes (no tag above) skip the matrix so CI does not run on every commit.

**Manual runs:** **Actions → Build wheels → Run workflow** runs the build matrix only; publishing is by **push** with `[pub pypi]` or `[pub test.pypi]` in the commit message.

**Authentication (publish):** the workflow uses **Trusted Publishing (OIDC)** by default — no API token in GitHub secrets. Register the GitHub repo and workflow file on each index (project **Manage → Publishing**). See the comment block at the top of `build-wheels.yml` for OIDC vs API-token options.

Private repositories are supported for OIDC the same as public ones.

## Troubleshooting

### MinGW Compilation Issues

#### SIZEOF_VOID_P Enum Error (Fixed)

**Problem:** When compiling with MinGW, you may encounter:
```
error: enumerator value for '__pyx_check_sizeof_voidp' is not an integer constant
```

**Root Cause:** MinGW's GCC is stricter about constant expressions in enum initializers. The Cython-generated code includes:
```c
enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) };
```

MinGW doesn't always evaluate `sizeof(void*)` as a compile-time constant in this context, even though both values are 8.

**Solution:** This has been fixed in `setup.py` by adding the `-DMS_WIN64` compiler flag, which explicitly tells GCC we're targeting 64-bit Windows. This ensures `sizeof(void*)` is treated as a compile-time constant.

The fix is already applied - no action needed. If you still see this error:
1. Ensure your Python is 64-bit: `python -c "import sys; print('64-bit' if sys.maxsize > 2**32 else '32-bit')"`
2. Ensure your MinGW GCC is 64-bit: `gcc -v` should show `x86_64`
3. Both must be consistently 64-bit (no 32/64-bit mismatch)

**References:**
- [Cython Issue #3405](https://github.com/cython/cython/issues/3405)
- [Cython Issue #4492](https://github.com/cython/cython/issues/4492)

#### Other MinGW Issues

**"mingw32 compiler not found":**
- Make sure MinGW is in PATH: `gcc --version`
- Run: `python configure_mingw.py`
- Check that `pydistutils.cfg` exists in your home directory

**"gcc: command not found":**
- Add MinGW bin directory to PATH:
  ```bash
  set PATH=C:\msys64\mingw64\bin;%PATH%
  ```

### MSVC Issues

**"Microsoft Visual C++ 14.0 or greater is required":**
- Install [Microsoft C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
- Select "Desktop development with C++" workload
- Restart your terminal after installation

### General Issues

**"No module named 'atabet_token'":**
- Run: `pip install -e .[dev]` to install the package

**Build succeeds but import fails:**
- Make sure you ran: `pip install -e .[dev]`
- Check that `.pyd` file exists in `atabet_token` directory
- Try: `python -c "import atabet_token.token"`

## Compiler Setup

### Option 1: MinGW with MSYS2 (Recommended if you have MSYS2)

**Pros:**
- Lightweight (~100MB)
- Works well with Cython
- Already installed if you have MSYS2

**Setup:**
```bash
# Run configuration script
python configure_mingw.py

# Or manually add to PATH
set PATH=C:\msys64\mingw64\bin;%PATH%
```

### Option 2: Microsoft C++ Build Tools

**Pros:**
- Official Microsoft compiler (best compatibility)
- Works out of the box
- Faster compilation

**Setup:**
1. Download from: https://visualstudio.microsoft.com/visual-cpp-build-tools/
2. Install "Desktop development with C++" workload
3. Restart terminal

## Project Structure

```
atabet-token/
├── atabet_token/              # Source code (Cython modules)
│   ├── __init__.py        # Package initializer
│   ├── token.pyx          # Cython source (compiled to .pyd)
│   └── licensing.py       # Python source (compiled to .pyd)
├── tests/                 # Test suite
│   ├── conftest.py        # Pytest configuration
│   ├── test_token.py      # Tests for original validation
│   └── test_licensing.py  # Tests for licensing validation
├── .github/
│   └── workflows/
│       └── build_wheels.yml  # CI/CD workflow
├── setup.py               # Build configuration (compiles all sources)
├── build_wheel.py         # Wheel protection script
├── pyproject.toml         # Modern Python packaging config
├── environment.yml        # Conda environment definition
├── MANIFEST.in            # Package manifest
├── configure_mingw.py     # MinGW configuration helper
├── build_with_mingw.bat    # MinGW build script
├── build_and_test.bat     # Build and test script
├── cleanup.bat            # Cleanup script
└── README.md              # This file
```

## Development

### Building from Source

```bash
conda activate atabet_token_env

# With MinGW
python setup.py build_ext --inplace --compiler=mingw32
pip install -e .[dev]

# With MSVC (default)
pip install -e .[dev]
```

### Running Tests

```bash
pytest -v
```

## Token Validation Methods

`atabet-token` supports **two validation methods**:

### 1. Licensing Server Validation (Default)

Validates tokens through a licensing server API. This is the **default validation method**.

**Usage:**
```python
from atabet_token import TokenCheckMeta

# Default: uses licensing validation
class MyClass(metaclass=TokenCheckMeta(application_id="my-app-id")):
    def __init__(self, token: str):
        self.token = token

# Or explicitly specify
class MyClass(metaclass=TokenCheckMeta(
    validation_method='licensing',
    application_id="my-app-id"
)):
    def __init__(self, token: str):
        self.token = token
```

**Configuration:**
- Set `LICENSING_SERVER_URL` environment variable (default: `http://39.101.65.167:5001/`; production reference: `http://api.atabet.com:5001`)
- Provide `application_id` in `TokenCheckMeta`
- Optional: `client_identifier`, `session_id`, `licensing_server_url`

**Requirements:**
- Licensing server must be running and accessible
- Token must be a valid JWT token issued by the licensing server
- `requests` package (automatically installed)

### 2. Original HMAC-SHA256 Validation

Validates tokens using the original HMAC-SHA256 method (monthly token rotation).

**Usage:**
```python
from atabet_token import TokenCheckMeta

# Explicitly use original validation
class MyClass(metaclass=TokenCheckMeta(
    validation_method='original',
    token_key="my-token-key"
)):
    def __init__(self, token: str):
        self.token = token
```

**Configuration:**
- Provide `token_key` in `TokenCheckMeta`
- Optional: `current_time` (for testing only)

**Note:** The original validation method is still fully supported and works exactly as before. Use `validation_method='original'` to explicitly enable it.

## Source Code Protection

`atabet-token` implements comprehensive source code protection to prevent reverse engineering. All source files are compiled and removed from distribution wheels.

### File Type Differences

#### `.pyc` Files (Python Bytecode)
- **What they are**: Compiled Python bytecode files
- **Security**: ⚠️ **NOT SECURE** - Can be easily decompiled back to Python source
- **Tools to decompile**: `uncompyle6`, `decompyle3`, `pycdc`
- **When used**: Created from `.py` files by Python's standard compiler

#### `.pyd` Files (Compiled C Extensions)
- **What they are**: Compiled C extensions (Windows) / `.so` files (Linux/Mac)
- **Security**: ✅ **SECURE** - Much harder to reverse engineer (requires disassembling machine code)
- **Tools to decompile**: Very difficult, requires advanced reverse engineering skills
- **When used**: Created from `.pyx` files (Cython source) or `.py` files compiled with Cython

### Protection Strategy

**Compilation Behavior:**
- **`.pyx` files** → Compiled to `.pyd`/`.so` (C extension) - **SECURE**
- **`.py` files** (including `__init__.py`) → Compiled to `.pyd`/`.so` (C extension) - **SECURE**
- **All `.pyc` files are removed** - They can be decompiled, so they are not included in distributions

**Note on `__init__.py`:**
- `__init__.py` files are compiled to `.pyd`/`.so` via Cython (same as other `.py` files)
- All `.pyc` files are removed from the distribution for security
- Python's import system works with compiled extensions (`.pyd`/`.so`) just as well as with source files

**Source File Removal:**
- After compilation, all source files (`.py`, `.pyx`, `.c`) are removed from wheel distributions
- **All `.pyc` files are also removed** (they can be decompiled back to source code)
- Only compiled C extensions (`.pyd`/`.so`) remain - these are secure and cannot be easily decompiled
- Package functions identically - users can import and use it normally
- Source code is completely hidden from end users

### Security Comparison

| Format | Security Level | Decompilation Difficulty |
|--------|---------------|-------------------------|
| `.py` (source) | ❌ None | N/A |
| `.pyc` (bytecode) | ⚠️ Low | Easy (tools available) |
| `.pyd` (C extension) | ✅ High | Very Hard (requires reverse engineering) |

### Implementation

The protection is implemented in:
1. **`setup.py`**: Compiles `.py` files as Cython extensions
2. **`build_wheel.py`**: Post-processes wheels to remove source files
3. **GitHub Actions**: Automatically protects wheels during CI/CD

### Verification

To verify protection:
1. Build a wheel: `python setup.py bdist_wheel`
2. Extract the wheel: `unzip dist/atabet_token-*.whl`
3. Check contents: Should see **only** `.pyd`/`.so` files, **no** `.py`, `.pyx`, `.pyc`, or `__pycache__` directories

## License

Proprietary - All Rights Reserved
