Add your description here
Project description
UserPermission
ユーザーとグループを管理するための非同期Pythonライブラリです。
- aiosqlite による非同期SQLiteデータベース管理
- pwdlib (Argon2) によるパスワードハッシュ化
- PyJWT によるJWTトークンの発行・検証
インストール
uv sync
# FastAPI連携を使う場合
uv sync --extra fastapi
# サーバーとして起動する場合(uvicorn含む)
uv sync --extra server
# Web管理画面を使う場合(FastAPI + Jinja2 + HTMX + Tailwind CSS)
uv sync --extra webui
# リレークライアントを使う場合(httpx含む)
uv sync --extra relay
使い方
初期化
import asyncio
from user_permission import Database
async def main():
# 初回実行時にシークレットキーを自動生成(以降はファイルから読み込み)
async with Database("app.db", secret="secret.key") as db:
# db.users / db.groups ですぐに使える
user = await db.users.create("alice", "password123")
group = await db.groups.create("admins")
asyncio.run(main())
ユーザー管理
# 作成
user = await db.users.create("alice", "password123", display_name="Alice")
# 取得
user = await db.users.get_by_id(1)
user = await db.users.get_by_username("alice")
# 一覧
users = await db.users.list_all()
# 更新(パスワード変更など)
await db.users.update(user.id, password="new_password")
await db.users.update(user.id, display_name="Alice Smith")
# 無効化
await db.users.update(user.id, is_active=False)
# 削除
await db.users.delete(user.id)
認証・トークン
# ログイン認証(成功時にJWTトークンを返す、失敗時はNone)
token = await db.users.authenticate("alice", "password123")
# トークンの有効期限を指定
from datetime import timedelta
token = await db.users.authenticate("alice", "password123", expires_delta=timedelta(hours=24))
# トークン検証
payload = db.token_manager.verify_token(token)
print(payload["sub"]) # ユーザーID(文字列)
print(payload["username"]) # ユーザー名
グループ管理
# 作成
group = await db.groups.create("admins", description="Administrator group")
# 取得
group = await db.groups.get_by_id(1)
group = await db.groups.get_by_name("admins")
# 一覧
groups = await db.groups.list_all()
# 更新
await db.groups.update(group.id, description="Updated description")
# 削除
await db.groups.delete(group.id)
グループメンバー管理
# ユーザーをグループに追加
await db.groups.add_user(group.id, user.id)
# ユーザーをグループから削除
await db.groups.remove_user(group.id, user.id)
# グループのメンバー一覧
members = await db.groups.get_members(group.id)
# ユーザーの所属グループ一覧
groups = await db.groups.get_user_groups(user.id)
サーバー起動
user-permission[server] でインストールすると、CLIからサーバーを起動できます。
uv run user-permission serve --host localhost --port 8001
| オプション | デフォルト | 説明 |
|---|---|---|
--host |
127.0.0.1 |
バインドアドレス |
--port |
8000 |
バインドポート |
--database |
user_permission.db |
SQLiteデータベースのパス |
--secret |
secret.key |
シークレットキーファイルのパス |
--prefix |
(なし) | APIルートプレフィックス(例: /api) |
--webui |
無効 | Web管理画面(HTMX+Tailwind+Jinja2)を有効化 |
--webui-prefix |
/ui |
管理画面のURLプレフィックス |
# APIと管理画面を同時に起動
uv run user-permission serve --prefix /api --webui
# → API: http://localhost:8000/api/*
# → 管理画面: http://localhost:8000/ui/
リレー(中継)
user-permission[relay] でインストールすると、Database に URL を渡すだけで、
ローカル SQLite と中央の UserPermission サーバーを同じインターフェースで切り替えられます。
from user_permission import Database
# ファイルパス → ローカル SQLite
db = Database("app.db", secret="secret.key")
# URL → リレー(リモートサーバーへ HTTP 中継)
db = Database("http://localhost:8001")
どちらでも db.users / db.groups の API は共通です。
async with Database("http://localhost:8001") as db:
# ログイン
token = await db.users.authenticate("alice", "password123")
# トークン検証
user = await db.verify_token(token)
# ユーザー・グループ操作(認証トークン付き)
users = await db.users.list_all(token)
group = await db.groups.create("admins", "Admin group", token)
await db.groups.add_user(group.id, user.id, token)
リレールーター(FastAPIアプリに中継ルーターをマウント)
別のFastAPIアプリにマウントすると、全リクエストが中央サーバーへ透過的に中継されます。
from contextlib import asynccontextmanager
from fastapi import FastAPI
from user_permission import Database, create_relay_router
db = Database("http://localhost:8001")
@asynccontextmanager
async def lifespan(app: FastAPI):
await db.connect()
yield
await db.close()
app = FastAPI(lifespan=lifespan)
app.include_router(create_relay_router(db, prefix="/auth"))
# /auth/token, /auth/me, /auth/users, ... が全て中央サーバーへ中継される
FastAPI連携
user-permission[fastapi] でインストールすると、ルーターを追加するだけでREST APIが使えます。
from contextlib import asynccontextmanager
from fastapi import FastAPI
from user_permission import Database, create_router
db = Database("app.db", secret="secret.key")
@asynccontextmanager
async def lifespan(app: FastAPI):
await db.connect()
yield
await db.close()
app = FastAPI(lifespan=lifespan)
app.include_router(create_router(db, prefix="/api"))
エンドポイント一覧
| メソッド | パス | 説明 | 認証 |
|---|---|---|---|
| POST | /api/token |
ログイン(トークン取得) | 不要 |
| GET | /api/me |
現在のユーザー情報(is_admin を含む) |
必要 |
| POST | /api/users |
ユーザー作成 | 不要 |
| GET | /api/users |
ユーザー一覧 | 必要 |
| GET | /api/users/{id} |
ユーザー取得 | 必要 |
| PATCH | /api/users/{id} |
ユーザー更新 | 本人 or 管理者 |
| DELETE | /api/users/{id} |
ユーザー削除 | 本人 or 管理者 |
| POST | /api/groups |
グループ作成 | 管理者 |
| GET | /api/groups |
グループ一覧 | 必要 |
| GET | /api/groups/{id} |
グループ取得 | 必要 |
| PATCH | /api/groups/{id} |
グループ更新 | 管理者 |
| DELETE | /api/groups/{id} |
グループ削除 | 管理者 |
| POST | /api/groups/{id}/members |
メンバー追加(管理者グループへの追加が昇格) | 管理者 |
| DELETE | /api/groups/{id}/members/{user_id} |
メンバー削除(管理者グループから外すと降格) | 管理者 |
| GET | /api/groups/{id}/members |
メンバー一覧 | 必要 |
| GET | /api/users/{id}/groups |
所属グループ一覧 | 必要 |
管理者ロール
UserPermission サーバー自身の管理権限(ユーザー/グループ/管理者の管理)は groups.is_admin = 1 のグループで表現します。
このフラグが立った管理者グループに所属しているユーザーが「UserPermission 管理者」です。
- 管理者は他ユーザーの編集・削除、グループの作成・更新・削除、メンバー管理が可能
- 他ユーザーの管理者昇格/降格は、管理者グループへの加入/脱退で行う
- 管理者グループは複数作れる(運用で分けたい場合)
- 消費サービス側の「アプリ管理者」などの概念はこの権限とは別で、通常のグループ(
is_admin = 0)で自由に表現してください
初回セットアップ
最初に作成されたユーザーは自動的に管理者グループに加入します。admin という名前のグループが無ければ、is_admin = 1 で新規作成されます。
# 新しいDBで最初のユーザーを登録するだけで管理者になる
uv run user-permission serve --database app.db --secret secret.key --webui
# ブラウザで /ui/register から alice を作成 → 自動的に管理者
既存DBのマイグレーション
v0.2.0 以降は起動時に groups.is_admin 列の存在を確認し、無ければ ALTER TABLE で追加します。既存データは壊しません。
既存のDBには管理者がまだ存在しないため、Python から手動で昇格させます。
import asyncio
from user_permission import Database
async def main():
async with Database("app.db", secret="secret.key") as db:
# 既存の好きなグループを管理者グループにする
group = await db.groups.get_by_name("admins")
await db.groups.update(group.id, is_admin=True)
# あるいは新規に作って任意ユーザーを加える
admin_group = await db.groups.create("admin", "管理者", is_admin=True)
user = await db.users.get_by_username("alice")
await db.groups.add_user(admin_group.id, user.id)
asyncio.run(main())
Web管理画面
user-permission[webui] でインストールすると、ブラウザ上でアカウント・グループを管理できる画面が追加されます。
FastAPI + Jinja2 + HTMX + Tailwind CSS(CDN)で構成されており、create_webui_router をマウントするか、
CLI の --webui フラグで有効化できます。
from contextlib import asynccontextmanager
from fastapi import FastAPI
from user_permission import Database, create_router, create_webui_router
db = Database("app.db", secret="secret.key")
@asynccontextmanager
async def lifespan(app: FastAPI):
await db.connect()
yield
await db.close()
app = FastAPI(lifespan=lifespan)
app.include_router(create_router(db, prefix="/api"))
app.include_router(create_webui_router(db, prefix="/ui"))
# → /ui/login, /ui/register, /ui/users, /ui/groups, /ui/me ...
create_app(webui=True) を使えばAPIと管理画面を一括で有効化できます。
from user_permission import create_app
app = create_app(
database="app.db",
secret="secret.key",
prefix="/api",
webui=True,
webui_prefix="/ui",
)
画面一覧
| パス | 説明 |
|---|---|
/ui/login |
ログイン |
/ui/register |
新規アカウント登録(登録と同時にログイン) |
/ui/logout |
ログアウト(Cookieを破棄) |
/ui/ |
ダッシュボード(ユーザー数・グループ数・所属グループ) |
/ui/me |
プロフィール編集 / パスワード変更 / 所属グループ |
/ui/users |
ユーザー一覧・作成(管理者は他ユーザーの編集・削除・有効/無効切替・管理者昇格/降格が可能) |
/ui/groups |
グループ一覧(作成・削除は管理者のみ) |
/ui/groups/{id} |
グループ編集・メンバー追加/削除(管理者のみ) |
認証はHTTPOnly Cookie(JWT)で管理され、トークンの有効期限は create_webui_router(token_expires=...) で調整できます(デフォルト24時間)。
管理者グループには一覧で「🔑 管理者」バッジが表示されます。管理者昇格/降格は、ユーザー一覧行のボタンから行えます(内部的には管理者グループへの加入/脱退)。
データベーススキーマ
| テーブル | 説明 |
|---|---|
users |
ユーザー情報(username は UNIQUE) |
groups |
グループ情報(name は UNIQUE) |
user_groups |
ユーザーとグループの多対多リレーション(複合PRIMARY KEY) |
ユーザーまたはグループを削除すると、関連する user_groups レコードも自動的に削除されます(CASCADE)。
依存パッケージ
- aiosqlite - 非同期SQLite
- pwdlib[argon2] - パスワードハッシュ化
- PyJWT - JWTトークン
オプション(user-permission[fastapi]):
- FastAPI - Web APIフレームワーク
- python-multipart - フォームデータ解析
オプション(user-permission[webui]):
- 上記FastAPI依存に加えて:
- Jinja2 - テンプレートエンジン(HTMX + Tailwind CSS はCDNから配信)
オプション(user-permission[server]):
- 上記FastAPI・Jinja2依存に加えて:
- uvicorn - ASGIサーバー
オプション(user-permission[relay]):
- httpx - 非同期HTTPクライアント
ライセンス
MIT OR Apache-2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file user_permission-0.3.0.tar.gz.
File metadata
- Download URL: user_permission-0.3.0.tar.gz
- Upload date:
- Size: 25.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
56a93ce7e43d64e617efb7feebf6486f81da3e4a94a75f2eafbf3e66c4ca6175
|
|
| MD5 |
0f2bc046f1d512819bd402a5b3dab4b3
|
|
| BLAKE2b-256 |
93d7276cec637a08189e699eef3e0131ae4ad1b57ac605b796eba96b62442111
|
File details
Details for the file user_permission-0.3.0-py3-none-any.whl.
File metadata
- Download URL: user_permission-0.3.0-py3-none-any.whl
- Upload date:
- Size: 31.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
036b3436268623adb187f6d69ebe947efc93072130dfa84698daa47d47c441c7
|
|
| MD5 |
dcdb36f303e596de2a088181cbb457ae
|
|
| BLAKE2b-256 |
08b3112a8f26296cb39ce3bdacc9e2c795dde3f3c421a09c76ae510acf01796e
|