Skip to content

Commit

Permalink
Merge pull request #64 from wisniowa56/v0.8-dev
Browse files Browse the repository at this point in the history
v0.8
  • Loading branch information
oplik0 authored Oct 31, 2021
2 parents ef94e86 + 675ccfe commit 3c11df4
Show file tree
Hide file tree
Showing 32 changed files with 7,522 additions and 3,928 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
uses: actions/setup-python@v2
with:
python-version: "3.x"
- uses: actions/cache@v2
Expand All @@ -31,12 +31,16 @@ jobs:
if: ${{ !contains(github.ref, 'master') }}
id: version
uses: oplik0/gh-action-buildnum@v1
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
with:
gist_token: ${{ secrets.GIST_TOKEN }}
version_key: ${{ github.ref }}
set_env: true
- name: Automatically set dev version
if: ${{ !contains(github.ref, 'master') }}
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
run: |
echo ::set-env name=CHERRYDOOR_VERSION::$(echo $(echo "$GITHUB_REF" | egrep -woi "([0-9\.{dev}\-]+)" && echo "$BUILDNUM_FOR_VERSION") | tr -d " ")
- name: Build and publish
Expand Down
21 changes: 13 additions & 8 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- name: Set up Python 3.8
uses: actions/setup-python@v1
- uses: actions/checkout@v2
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.8
python-version: 3.9
- uses: actions/cache@v2
id: cache
with:
Expand All @@ -24,10 +24,15 @@ jobs:
run: |
python -m pip install --upgrade pip
printf 'n\ny\ny\nadmin\nadmin\n' | bash ./install.sh
- name: Black Code Formatter
uses: lgeiger/[email protected]
- name: Check files using the black formatter
uses: rickstaa/action-black@v1
id: action_black
with:
black_args: ". -t py39"
- name: Annotate diff changes using reviewdog
if: steps.action_black.outputs.is_formatted == 'true'
uses: reviewdog/action-suggester@v1
with:
args: ". -t py38 --check"
tool_name: blackfmt
- name: Bandit Security Linter
uses: jpetrucciani/bandit-check@master
3 changes: 2 additions & 1 deletion cherrydoor/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Run the server"""
"""Run the server."""

__author__ = "opliko"
__license__ = "MIT"
Expand All @@ -17,6 +17,7 @@


def cherrydoor():
"""Run the server."""
parser = argparse.ArgumentParser(
prog="cherrydoor", description="Cherrydoor management"
)
Expand Down
2 changes: 1 addition & 1 deletion cherrydoor/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.7b10"
__version__ = "0.8.0"
82 changes: 70 additions & 12 deletions cherrydoor/api_tokens.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
"""
Create and validate Branca-based API tokens
"""
"""Create and validate Branca-based API tokens."""

__author__ = "opliko"
__license__ = "MIT"
__version__ = "0.7b0"
__version__ = "0.8.b0"
__status__ = "Prototype"

import logging
Expand All @@ -13,7 +11,6 @@

import msgpack
from aiohttp.web import HTTPForbidden, HTTPUnauthorized
from aiohttp_security import check_permission
from branca import Branca

from cherrydoor.database import (
Expand All @@ -26,16 +23,44 @@


class ApiTokens:
"""A class for managing branca-based API tokens."""

def __init__(self, app, secret):
"""Initialize the Api Token manager.
Parameters
----------
app : aiohttp.web.Application
The aiohttp application instance
secret : str
The secret key used to sign the API tokens (after hashing)
"""
self.app = app
self.branca = Branca(key=sha3_256(secret.encode("utf-8")).digest())
logger.debug("created Branca instance")

async def generate_token(self, username, token_name=str(uuid4()), permissions="*"):
"""Generate a token for a user with given permissions.
Parameters
----------
username : str
The username of the user for whom the token is being generated
token_name : str, default=str(uuid4())
The name of the token to be generated - will represent it on the frontend
permissions : str, default="*"
The permissions that the token will have acces to. "*" means all permissions user has
Returns
-------
(token, token_name, permissions) : tuple(str, str, list(str))
The generated token, its name and permissions it has access to
"""
if not token_name:
token_name = str(uuid4())
logger.debug(
f"generating a token for {username} with these permissions: {permissions}"
"generating a token for %s with these permissions: %s",
username,
permissions,
)
user = await find_user_by_username(
self.app, username, ["permissions", "password", "_id"]
Expand All @@ -54,7 +79,9 @@ async def generate_token(self, username, token_name=str(uuid4()), permissions="*
)
token = self.branca.encode(packed)
logger.debug(
f"successfuly generated a token for {username}: {token}. Adding it to the database"
"successfuly generated a token for %s: %s. Adding it to the database",
username,
token,
)
await add_token_to_user(self.app, user.get("_id", ""), token_name, token)
return (
Expand All @@ -63,15 +90,33 @@ async def generate_token(self, username, token_name=str(uuid4()), permissions="*
permissions,
)

async def validate_token(self, token, permissions=[], raise_error=False):
async def validate_token(self, token, permissions=None, raise_error=False):
"""Validate if token is valid and has the requested permissions.
Parameters
----------
token : str
The token to be validated
permissions : list(str), default=None
The permissions that the token must have access to.
raise_error : bool, default=False
If True, raise an error if the token is invalid, otherwise just return False
Returns
-------
bool
True if the token is valid and has the requested permissions, False otherwise
"""
permissions = permissions if permissions else []
logger.debug(
f"validating a token{' for permissions' + permissions if permissions != [] else ''}. Token: {token}"
"validating a token%s. Token: %s",
" for permissions" + permissions if permissions != [] else "",
token,
)
user = await find_user_by_token(self.app, token, ["permissions", "username"])
packed = self.branca.decode(token)
payload = msgpack.loads(packed, raw=False)
logger.debug(f"decoded token: {payload}")
if user == None:
logger.debug("decoded token: %s", payload)
if user is None:
if raise_error:
raise HTTPUnauthorized()
return False
Expand All @@ -94,10 +139,23 @@ async def validate_token(self, token, permissions=[], raise_error=False):
return True

async def get_token_info(self, token):
"""Get information about a given token. Includes permissions, username, token name, etc.
Parameters
----------
token : str
The token to be checked
Returns
-------
(payload, user) : tuple(dict, dict)
A dictionary containing information about the token and a dict with user information
(username, user permissions)
"""
user = await find_user_by_token(self.app, token, ["permissions", "username"])
packed = self.branca.decode(token)
payload = msgpack.loads(packed, raw=False)
if user == None:
if user is None:
return payload, None
user_permission_set = set(user.get("permissions", []))
if not set(payload.get("permissions", [])).issubset(user_permission_set):
Expand Down
62 changes: 54 additions & 8 deletions cherrydoor/app.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
"""
Everything connected to setup of the app
"""
"""Everything connected to setup of the app."""

__author__ = "opliko"
__license__ = "MIT"
__version__ = "0.7"
__version__ = "0.8.b0"
__status__ = "Prototype"

import asyncio
Expand Down Expand Up @@ -52,6 +50,19 @@


def setup_app(loop=asyncio.get_event_loop(), config=load_config()[0]):
"""Create the app and initiate all services (database, security, etc).
Parameters
----------
loop : asyncio.EventLoop
The event loop to use.
config : AttrDict
a dictionary with configuration values
Returns
-------
app : web.Application
The application instance.
"""
if config.get("sentry_dsn", None):
sentry_sdk.init(
dsn=config["sentry_dsn"],
Expand Down Expand Up @@ -129,6 +140,13 @@ def setup_app(loop=asyncio.get_event_loop(), config=load_config()[0]):


def setup_static_routes(app):
"""Set the mapping of system static items path to /static url.
Parameters
----------
app : web.Application
The application instance.
"""
app.router.add_static(
"/static/",
path=f"{os.path.dirname(os.path.realpath(__file__))}/static",
Expand All @@ -140,11 +158,34 @@ def setup_static_routes(app):


def setup_routes(app):
"""Set all the routes for the app using RouteTableDef.
Notes
-----
Currently imported files with routes:
- views.py
Parameters
----------
app : web.Application
The application instance.
"""
app.add_routes(views)


@contextfunction
def sri_for(context: Dict[str, Any], static_file_path: str) -> str:
def sri_for(
context: Dict[str, Any], static_file_path: str # pylint: disable=unused-argument
) -> str:
"""
Return a hash in the format used by the SRI HTML attribute.
Parameters:
context (dict): The template context.
static_file_path (str): The path to the static file.
Returns:
sri (str): The SRI hash in sha256 format.
"""
input_file = (
f"{os.path.dirname(os.path.realpath(__file__))}/static/{static_file_path}"
)
Expand All @@ -161,8 +202,13 @@ def sri_for(context: Dict[str, Any], static_file_path: str) -> str:


def vue(item):
"""
Filter out vue templates
For example: {{ "message.text" | vue }} will be transformed to just {{ "message.text" }}
"""Filter out vue templates.
For example: {{ "message.text" | vue }} will be transformed to just {{ "message.text" }} in HTML
Parameters:
item (str): The text to filter.
Returns:
item (str): Text that jinja2 will render properly.
"""
return f"{{{{ {item} }}}}"
Loading

0 comments on commit 3c11df4

Please sign in to comment.