Skip to main content

Plugin to allow users to define their own profiles

Project description

datasette-user-profiles

PyPI Changelog Tests License

Plugin to allow users to define their own profiles

Installation

Install this plugin in the same environment as Datasette.

datasette install datasette-user-profiles

Usage

Usage instructions go here.

Locking fields users can't edit

Every profile field is user-editable by default. When another part of your stack is the authoritative source for a field — e.g. a GitHub auth plugin that already puts the user's email (and name, avatar, ...) into the actor JSON — you can stop users from overwriting it from the profile editor.

Set editable_fields in plugin config. Each field defaults to true (editable); list only the ones you want to lock:

plugins:
  datasette-user-profiles:
    editable_fields:
      email: false
      display_name: false
      avatar: false      # covers the uploaded photo and the generated icon/color
      bio: true

Locked fields are enforced server-side — the update and photo endpoints ignore or reject changes to them, not just hide the inputs — and the edit page renders them disabled.

Acting as the actor directory

datasette-user-profiles is the canonical user directory for this stack. Every "who are the users?" surface — the share dialog's add-a-collaborator box, @mentions, acl's actor picker, author chips, audit "shared by" lines — draws from this one indexed, agent-aware directory rather than maintaining its own ad-hoc list. It exposes three consumer-facing contracts:

1. Search / autocomplete endpoint

GET /-/profiles/api/search?q=<text>&limit=<n>&email=<0|1>

Gated by the profile_access permission (the same gate as every other profile endpoint). Parameters:

  • q — free-text query. Matched (case-insensitively) against display_name, email, and actor_id. Empty/absent q returns the most-recently-updated profiles instead.
  • limit — max results, defaults to 20, capped at 50 (and floored at 1).
  • email — set to 0/false/no/off to omit emails from results (defaults to including them).

Display-name prefix matches rank ahead of contains-only matches, then results are alphabetical by display_name. Response shape:

{
  "results": [
    {
      "id": "alice",
      "display_name": "Alice Anderson",
      "email": "alice@example.com",
      "avatar_url": "/-/profile/pic/alice",
      "kind": "user"
    }
  ]
}

kind is always "user" — profiles only knows users. Callers that also want agents (or other identities) query those sources separately and merge client-side; profiles stays decoupled from the agent directory.

2. resolve_profile_actors() output shape

resolve_profile_actors(datasette, actor_ids) (see "Actor resolution" below) returns a {actor_id: {...}} map containing only the IDs that have a profile. Known users resolve to:

{
  "id": "alice",
  "display_name": "Alice Anderson",
  "email": "alice@example.com",
  "kind": "user",
  "avatar_url": "/-/profile/pic/alice"
}

IDs without a matching profile are omitted from the map — the caller decides how to fall back (typically a bare {"id": <id>}).

Consolidation note

This directory replaces three previously-scattered user-listing mechanisms:

Old mechanism Now drawn from
acl datasette_acl_valid_actors (no query, all actors) the profiles search API (acl admin UI may keep the hook as a fallback)
comments datasette_comments_users hook + startswith filtering the profiles search API
comments' private from datasette_user_profiles.routes.pages import get_profile resolve_profile_actors(...)

The old hooks keep working for one release as a fallback when profiles is not installed, then they are retired.

As a cheap convenience, when datasette-acl is installed this plugin also implements acl's datasette_acl_valid_actors hook, returning every profile as an {"id", "display"} dict so acl's standalone admin pages get nicer displays. That hookimpl is only registered if datasette_acl is importable, so profiles has no hard dependency on acl.

Actor resolution (resolve_profile_actors)

This plugin does not implement Datasette's core actors_from_ids hook. That hook is declared firstresult=True, so the first plugin to implement it wins and every other identity source (agents, service accounts, remote directories) is locked out. Rather than silently seize that hook just by being installed, profiles exposes its resolution logic as a plain function you can opt into:

from datasette_user_profiles import resolve_profile_actors

actors = await resolve_profile_actors(datasette, ["alice", "agent-1"])
# {"alice": {"id": "alice", "display_name": "Alice Anderson",
#            "email": "alice@example.com", "kind": "user",
#            "avatar_url": "/-/profile/pic/alice"}}

It returns a {actor_id: {...}} map for the IDs that have a profile, and omits the rest so you can merge it with other sources and apply your own fallback.

If you want profiles to back Datasette's core actors_from_ids, wire it up from a plugin you control — designating a single owner for the hook and choosing how to merge other identity sources:

from datasette import hookimpl
from datasette_user_profiles import resolve_profile_actors

@hookimpl
def actors_from_ids(datasette, actor_ids):
    async def inner():
        actors = await resolve_profile_actors(datasette, actor_ids)
        # ...merge in agents / service accounts / other directories here...
        for actor_id in actor_ids:
            actors.setdefault(str(actor_id), {"id": str(actor_id)})
        return actors
    return inner

Development

To set up this plugin locally, first checkout the code. You can confirm it is available like this:

cd datasette-user-profiles
# Confirm the plugin is visible
uv run datasette plugins

To run the tests:

uv run pytest

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

datasette_user_profiles-0.1.0a7.tar.gz (56.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

datasette_user_profiles-0.1.0a7-py3-none-any.whl (64.5 kB view details)

Uploaded Python 3

File details

Details for the file datasette_user_profiles-0.1.0a7.tar.gz.

File metadata

File hashes

Hashes for datasette_user_profiles-0.1.0a7.tar.gz
Algorithm Hash digest
SHA256 80d22b24f5142c18c98890b363bc6a3c8c06f2a0155b759c1fcdf64d7ed75320
MD5 5e1dc6e51e3bb2aadc911e5ff3067fdc
BLAKE2b-256 0899ce642d0fac5e6a0b965b4ce53d0af351266da0b7f05acf2457ccec1a8cb8

See more details on using hashes here.

Provenance

The following attestation bundles were made for datasette_user_profiles-0.1.0a7.tar.gz:

Publisher: publish.yml on datasette/datasette-user-profiles

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file datasette_user_profiles-0.1.0a7-py3-none-any.whl.

File metadata

File hashes

Hashes for datasette_user_profiles-0.1.0a7-py3-none-any.whl
Algorithm Hash digest
SHA256 a54569ae64ae9713056a030d23fe25c34003447d944da497646e6edb2c3cc07b
MD5 b2302c6c9d6c44ee31d3b5224c52b512
BLAKE2b-256 303b6546ae22e4da1e28c3458e16abc22902b928d4d8cbbba8924e7d0d1b12b6

See more details on using hashes here.

Provenance

The following attestation bundles were made for datasette_user_profiles-0.1.0a7-py3-none-any.whl:

Publisher: publish.yml on datasette/datasette-user-profiles

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page