diff --git a/CHECKS b/CHECKS new file mode 100644 index 00000000..f75847a1 --- /dev/null +++ b/CHECKS @@ -0,0 +1,5 @@ +# WAIT=30 +# TIMEOUT=10 +# ATTEMPTS=60 # 60 * 30 = 30 min + +# / Playground diff --git a/Dockerfile b/Dockerfile index 2365f893..3f0887f3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,13 @@ -FROM python:3.8 - -EXPOSE 8080 - -RUN /usr/local/bin/python -m pip install --upgrade pip - -WORKDIR /usr/src/app - -COPY requirements.txt ./ - -RUN set -ex && pip install -r requirements.txt - -COPY . . - -CMD ["python", "server.py"] +FROM python:3.8 + +EXPOSE 8080 + +RUN /usr/local/bin/python -m pip install --upgrade pip + +WORKDIR /usr/src/app + +COPY requirements.txt ./ + +RUN set -ex && pip install -r requirements.txt + +COPY . . diff --git a/Procfile b/Procfile new file mode 100644 index 00000000..c5c1bfa8 --- /dev/null +++ b/Procfile @@ -0,0 +1,2 @@ +web: python server.py + diff --git a/auth/identity.py b/auth/identity.py index c2526863..197f9fd7 100644 --- a/auth/identity.py +++ b/auth/identity.py @@ -1,3 +1,6 @@ +from binascii import hexlify +from hashlib import sha256 + from jwt import DecodeError, ExpiredSignatureError from passlib.hash import bcrypt from sqlalchemy import or_ @@ -12,16 +15,40 @@ from validations.auth import AuthInput class Password: @staticmethod - def encode(password: str) -> str: + def _to_bytes(data: str) -> bytes: + return bytes(data.encode()) - # TODO: sha256 -> hexdigest -> bcrypt - return bcrypt.hash(password) + @classmethod + def _get_sha256(cls, password: str) -> bytes: + bytes_password = cls._to_bytes(password) + return hexlify(sha256(bytes_password).digest()) + + @staticmethod + def encode(password: str) -> str: + password_sha256 = Password._get_sha256(password) + return bcrypt.using(rounds=10).hash(password_sha256) @staticmethod def verify(password: str, hashed: str) -> bool: - # TODO: detect rounds amount - # TODO: sha256 -> hexdigest -> bcrypt - return bcrypt.verify(password, hashed) + """ + Verify that password hash is equal to specified hash. Hash format: + + $2a$10$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm + \__/\/ \____________________/\_____________________________/ + | | Salt Hash + | Cost + Version + + More info: https://passlib.readthedocs.io/en/stable/lib/passlib.hash.bcrypt.html + + :param password: clear text password + :param hashed: hash of the password + :return: True if clear text password matches specified hash + """ + hashed_bytes = Password._to_bytes(hashed) + password_sha256 = Password._get_sha256(password) + + return bcrypt.verify(password_sha256, hashed_bytes) class Identity: diff --git a/resolvers/profile.py b/resolvers/profile.py index 6730ac53..1e68f27b 100644 --- a/resolvers/profile.py +++ b/resolvers/profile.py @@ -213,6 +213,6 @@ def get_top_authors(_, _info, offset, limit): @query.field("getAuthor") async def get_author(_, _info, slug): - a = await UserStorage.users[slug] - a.stat = get_author_stat(slug) + a = await UserStorage.get_user_by_slug(slug) + a.stat = await get_author_stat(slug) return a diff --git a/resolvers/topics.py b/resolvers/topics.py index e2216caf..12d914f8 100644 --- a/resolvers/topics.py +++ b/resolvers/topics.py @@ -55,8 +55,8 @@ async def topics_by_author(_, _info, author): @query.field("getTopic") async def get_topic(_, _info, slug): - t = await TopicStorage.topics[slug] - t.stat = get_topic_stat(slug) + t = TopicStorage.topics[slug] + t.stat = await get_topic_stat(slug) return t