User Manager¶
harbor.users provides user lookup, search, sorting, and pagination through
UserManager. Use it to build application-specific tools such as a user
management panel, support dashboard, or internal CLI.
Your application decides how to expose these operations, who may use them, and which user data is visible.
The examples below use an already configured UserHarbor instance named
harbor.
Looking up a user¶
Find an account by username or email:
user = harbor.users.get_by_username("alice")
user = harbor.users.get_by_email("alice@example.com")
if user is not None:
print(user.username, user.email, user.verified)
Both methods return UserT | None: the public user object, or None when the
account does not exist. Username lookup is case-insensitive and preserves the
stored username casing. Email lookup normalizes the supplied address to
lowercase, as registration does.
Use these methods when you know the account's username or email. To identify
the user of an authenticated request, use harbor.get_current_user(session_token).
Listing and searching users¶
users = harbor.users.list(
limit=25,
offset=0,
order="username",
query="alice",
)
for user in users.items:
print(user.username, user.email)
print(users.total)
The result is a UserList[UserT], defined in userharbor.interfaces:
itemscontains the users on the requested page.totalcounts all users matching the query before pagination. It remains available even when the requested page is empty.
query matches a literal substring of either the username or email, without
case sensitivity. Leading and trailing whitespace is ignored. None, an empty
string, or whitespace alone lists all users. Characters such as % and _
are treated as ordinary characters.
Pagination¶
limit defaults to 50 and must be between 1 and 500. offset defaults
to 0 and must not be negative. Invalid pagination arguments raise ValueError.
Use total to calculate the page count for your interface:
page_size = 25
page_number = 2 # Pages in this example start at 1.
users = harbor.users.list(
limit=page_size,
offset=(page_number - 1) * page_size,
query="alice",
)
page_count = (users.total + page_size - 1) // page_size
Sorting¶
order defaults to "username". Pass a storage field name for ascending order
or prefix it with - for descending order. Both the SQLAlchemy and in-memory
adapters support username, email, and verified:
Usernames are ordered using casefold(). Equal sort values are ordered by
username ascending to keep pagination deterministic for an unchanged dataset.
Field names are passed to the store without a list of allowed fields.
Unsupported names raise an error. With the SQLAlchemy adapter, order names
an attribute of the database model, so custom model fields can also be used:
A field created only by user_mapper needs a corresponding sortable attribute
on the storage model; the manager does not translate public field names.
Building a user management panel¶
User manager reads do not require a session or a verified account, and do not
check the caller's permissions. Apply your application's access rules before
calling them. For example, if your application has configured and assigned a
users.read permission:
def list_users_for_admin(session_token, *, query=None, limit=25, offset=0):
harbor.require_permission(session_token, "users.read")
return harbor.users.list(
limit=limit,
offset=offset,
order="username",
query=query,
)
Your UI can display items in a table and use total for pagination. The same
operation can serve a web view, an API route, or an internal tool.
Custom user types¶
Lookups and listings preserve the public UserT returned by your store. With
the SQLAlchemy adapter, list items use the same user_mapper as individual
lookups, so custom public fields remain available to your application.
See Custom user mapping for an example.