Quart-Keycloak
zZzZ
Redis cache
app.setup_cache()
After which you may use it directly;
from quart_session.sessions import SessionInterface
@app.route("/")
async def hello():
cache: SessionInterface = app.session_interface
await cache.set("random_key", "val", expiry=3600)
data = await cache.get("random_key")
multi-subscriber websocket
from asyncio_multisubscriber_queue import MultisubscriberQueue
broadcast = MultisubscriberQueue()
await broadcast.put({"payload": 1})
async for msg in broadcast.subscribe():
[...]
Utils
from quart.kroket.utils import ...
def make_slug(inp: str)
def program_exists(name: str)
def safu(func) (decorator)
def get_visitor_ipv4(enforce_forwarded_header=True)
def random_str(num_chars: int)
image APIs
app.setup_image_apis()
Registers the following routes:
/_/avatar/<path:inp>/_/gravatar/<path:inp>/_/avatar/<path:inp>/_/qr/<path:inp>/<path:color_from>/<path:color_to>/(colors need to be tuple)
url_for("kquart.route_avatar", inp="test")
url_for("kquart.route_gravatar", inp="test")
url_for("kquart.route_qr", inp="test")
Error templates
Create these:
templates/errors/500.htmltemplates/errors/403.htmltemplates/errors/404.html
OIDC
app.setup_keycloak(
client_id='',
client_secret='',
configuration='url')
auto-registered routes:
/oidc/login/oidc/logout/oidc/after_logout
After login, will set g.user and session['user'] with instance of quart.kroket.oidc.models.KeycloakUser
So in factory.py we'll have before_request look like:
@app.before_request
async def set_request_ctx():
from app.db.models import User
from quart.kroket.database.enums import UserRole
auth_token: KeycloakAuthToken = g.ses
if not auth_token:
return
if g.user:
return
# get and update (or create) user
try:
user: User = User.by_uid(auth_token.sub)
user.username = auth_token.username
user.role = UserRole.member
user.save()
g.user = user
except pw.DoesNotExist as ex:
user: User = User.create(
uuid=auth_token.sub,
username=auth_token.username,
role=UserRole.member)
g.user = user
except Exception as ex:
current_app.logger.error(ex)
session.clear()
raise Exception(ex)
Template filters
hash_sha256(val)
def hash_md5(val)
def size_human(val)
def dt_ago(val: datetime)
def dt_human(val)
Rate limiting
rate_limiter = RateLimiter()
def create_app():
app = Quart(__name__)
rate_limiter.init_app(app)
return app
@app.route('/')
@rate_limit(1, timedelta(seconds=10))
async def handler():
[...]
Database
Enum
Usage:
from enum import IntEnum, unique
@unique
class UserStatus(IntEnum):
disabled = 0
enable = 1
banned = 2
[...]
status = EnumIntField(enum_class=UserStatus, default=UserStatus.active)
[...]
Model.select().where(Model.status != UserStatus.banned)
Usage:
from enum import IntEnum, unique
@unique
class UserTags(IntEnum):
has_car = 0
has_plane = 1
has_helicopter = 2
[...]
vehicles = EnumArrayField(enum_class=UserTags, field_class=IntegerField, default=[UserTags.has_bank_account])
# Fetch results with `has_car` OR `has_helicopter`:
Model.select().where(
Model.vehicles.contains_any(UserTags.has_car, UserTags.has_helicopter)).get()
database example
from quart.kroket.database import create_db
database = create_db()
class Company(pw.Model):
uid: Union[str, UUID] = pw.UUIDField(primary_key=True, default=uuid4)
created = pw.DateTimeField(default=datetime.now)
modified = pw.DateTimeField(default=datetime.now)
name: str = pw.CharField(unique=True, index=True, max_length=256)
users: List['User'] = None
class Meta:
from my_app.factory import database
database = database
class User(pw.Model):
uid: Union[str, UUID] = pw.UUIDField(primary_key=True) # uuid4 from keycloak
created = pw.DateTimeField(default=datetime.now)
modified = pw.DateTimeField(default=datetime.now)
name = pw.CharField(unique=True, index=True, max_length=256)
username = pw.CharField(unique=True, index=True, max_length=64)
company = pw.ForeignKeyField(Company, backref='users')
roles: List[UserRole] = EnumArrayField(enum_class=UserRole, field_class=pw.IntegerField, default=[UserRole.anonymous])
status: UserStatus = EnumIntField(enum_class=UserStatus, default=UserStatus.active)
things: List['Thing'] = None
class Meta:
from my_app.factory import database
database = database
@property
def is_admin(self):
return UserRole.admin in self.roles
@property
def is_anon(self):
return UserRole.anonymous in self.roles
@property
def has_auth(self):
return not self.is_anon
@staticmethod
def by_uid(uuid: Union[str, UUID]):
return User.select() \
.join(File, pw.JOIN.LEFT_OUTER) \
.where(User.uid == uuid).get()
# .join(Download, pw.JOIN.LEFT_OUTER) \
async def to_json(self):
return {
"uid": self.uid
}
class Thing(pw.Model):
uid: Union[str, UUID] = pw.UUIDField(primary_key=True, default=uuid4)
created = pw.DateTimeField(default=datetime.now)
modified = pw.DateTimeField(default=datetime.now)
title: str = pw.CharField(max_length=512, index=True)
markdown: str = pw.TextField(null=True)
roles_view: List[UserRole] = EnumArrayField(enum_class=UserRole, field_class=pw.IntegerField, default=[])
roles_edit: List[UserRole] = EnumArrayField(enum_class=UserRole, field_class=pw.IntegerField, default=[])
participants: List[User] = pw.ManyToManyField(User, backref='thing_participations')
ip = pw.IPField(index=True, null=True)
creator = pw.ForeignKeyField(User, backref='things')
class Meta:
from my_app.factory import database
database = database
@property
def ago(self):
return humanize.naturaltime(self.created)
@classmethod
async def new_ticket(cls, user: User, title: str, markdown: str, remote_address: str) -> 'Thing':
if not title:
raise Exception("title cannot be empty")
if not validate_ipv4(remote_address):
raise Exception("upload ipv4 address invalid")
return cls.create(
title=title,
markdown=markdown,
creator=user,
ip=remote_address
)
UserThingParticipant = Thing.participants.get_through_model()
Metadata
Release files for Quart-Kroket 1.0.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| Quart-Kroket-1.0.7.tar.gz | 298.7 kB | Without provenance | Details |
Release files / Quart-Kroket-1.0.7.tar.gz
| Download URL | Quart-Kroket-1.0.7.tar.gz |
|---|---|
| Size | 298.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d8e2ce27a3f847bfb08ba3d23c771f5576940fb6778aff40f49f4944dd189796
|
|
BLAKE2b-256 checksum How to use checksums |
bfb9ff187d504d106cf8e52f1d756eecde676236755fa907a5127298a159836c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.2 CPython/3.9.7
|
Security for Quart-Kroket 1.0.7
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.