mirror of
https://github.com/enpaul/keyosk.git
synced 2024-11-24 23:47:49 +00:00
Add token storage model
Document datatypes module Included initial permissions system in token model, will likely need to update/move it
This commit is contained in:
parent
f1254c4704
commit
33325a344e
103
keyosk/database/token.py
Normal file
103
keyosk/database/token.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
from collections import OrderedDict
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
import peewee
|
||||||
|
|
||||||
|
from keyosk import datatypes
|
||||||
|
from keyosk.database._shared import KeyoskBaseModel
|
||||||
|
from keyosk.database.account import Account
|
||||||
|
from keyosk.database.account_acl import AccountACLEntry
|
||||||
|
from keyosk.database.domain import Domain
|
||||||
|
|
||||||
|
|
||||||
|
class Token(KeyoskBaseModel):
|
||||||
|
class Meta:
|
||||||
|
table_name = "token"
|
||||||
|
|
||||||
|
account = peewee.ForeignKeyField(Account, backref="tokens")
|
||||||
|
domain = peewee.ForeignKeyField(Domain, backref="tokens")
|
||||||
|
issuer = peewee.CharField(null=False)
|
||||||
|
issued = peewee.DateTimeField(null=False, default=datetime.datetime.utcnow)
|
||||||
|
expires = peewee.DateTimeField(null=False)
|
||||||
|
revoked = peewee.BooleanField(null=False)
|
||||||
|
_claims = peewee.CharField(null=False)
|
||||||
|
_usage = peewee.CharField(null=False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def claims(self):
|
||||||
|
return json.loads(self._claims)
|
||||||
|
|
||||||
|
@claims.setter
|
||||||
|
def claims(self, value):
|
||||||
|
self._claims = json.dumps(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def usage(self) -> datatypes.TokenUsage:
|
||||||
|
return datatypes.TokenUsage[self._usage]
|
||||||
|
|
||||||
|
@usage.setter
|
||||||
|
def usage(self, value: datatypes.TokenUsage):
|
||||||
|
self._usage = value.name
|
||||||
|
|
||||||
|
def make_public_claims(self):
|
||||||
|
return {
|
||||||
|
"jti": self.uuid,
|
||||||
|
"sub": self.account.username,
|
||||||
|
"aud": self.domain.audience,
|
||||||
|
"iss": self.issuer,
|
||||||
|
"exp": int(self.expires.timestamp()),
|
||||||
|
"iat": int(self.issued.timestamp()),
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def factory(
|
||||||
|
cls,
|
||||||
|
account: Account,
|
||||||
|
domain: Domain,
|
||||||
|
issuer: str,
|
||||||
|
lifespan: datetime.timedelta,
|
||||||
|
usage: datatypes.TokenUsage,
|
||||||
|
permissions: Sequence[AccountACLEntry],
|
||||||
|
):
|
||||||
|
new = cls(
|
||||||
|
account=account,
|
||||||
|
domain=domain,
|
||||||
|
issuer=issuer,
|
||||||
|
expires=(datetime.datetime.utcnow() + lifespan),
|
||||||
|
usage=usage,
|
||||||
|
revoked=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
acls = {}
|
||||||
|
for permission in permissions:
|
||||||
|
# Note: Because we're relying on dictionary order here, we need to use
|
||||||
|
# ordered dict to support python3.6. Dictionaries remembering insertion
|
||||||
|
# order was officially implemented in 3.6, but not guaranteed until 3.7. So,
|
||||||
|
# technically, it would be fine to use a plain'ol'dictionary here, but to
|
||||||
|
# conform to best practices we use ordered dict for python3.6 support
|
||||||
|
# https://stackoverflow.com/questions/39980323/are-dictionaries-ordered-in-python-3-6
|
||||||
|
acls[permission.access_list.name] = OrderedDict(
|
||||||
|
{
|
||||||
|
item.name: False
|
||||||
|
for item in sorted(
|
||||||
|
domain.permissions, key=lambda item: item.bitindex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for permission in permissions:
|
||||||
|
acls[permission.access_list.name][permission.permission.name] = True
|
||||||
|
|
||||||
|
bitmasks = {
|
||||||
|
key: int("".join([str(int(item)) for item in value.values()]), 2)
|
||||||
|
for key, value in acls.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
claims = new.make_public_claims()
|
||||||
|
claims.update({"ksk-usg": new.usage.value, "ksk-pem": bitmasks})
|
||||||
|
|
||||||
|
new.claims = claims
|
||||||
|
|
||||||
|
return new
|
@ -1,3 +1,4 @@
|
|||||||
|
"""Shared types, enums, and data containers"""
|
||||||
import enum
|
import enum
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Union
|
from typing import Union
|
||||||
@ -7,11 +8,18 @@ Extras = Dict[str, Union[int, float, bool, str, None]]
|
|||||||
|
|
||||||
|
|
||||||
class TokenUsage(enum.Enum):
|
class TokenUsage(enum.Enum):
|
||||||
REFRESH = enum.auto()
|
"""Possible usage values for an issued JWT
|
||||||
ACCESS = enum.auto()
|
|
||||||
|
Values will be the value of the ``ksk-usg`` claim in the issued token
|
||||||
|
"""
|
||||||
|
|
||||||
|
REFRESH = "ref"
|
||||||
|
ACCESS = "acc"
|
||||||
|
|
||||||
|
|
||||||
@enum.unique
|
@enum.unique
|
||||||
class StorageBackend(enum.Enum):
|
class StorageBackend(enum.Enum):
|
||||||
|
"""Supported storage backends"""
|
||||||
|
|
||||||
SQLITE = "sqlite"
|
SQLITE = "sqlite"
|
||||||
MARIA = "maria"
|
MARIA = "maria"
|
||||||
|
Loading…
Reference in New Issue
Block a user