Commit 142e7603 authored by Michal Pavlík's avatar Michal Pavlík

full JWT security + added boards management

parent 5a3b1a20
## Unix
run bash unix.sh
## Windows
run windows.cmd
\ No newline at end of file
from models import Post
from models import Post, Board
from flask import request, escape
from config import db
from errors import BoardNotFound
def parse_page(data):
......@@ -12,18 +13,37 @@ def parse_page(data):
}
def post(user, token_info):
def create(user, token_info):
data = request.json
new_post = Post(message=escape(data['message']), author_id=user)
new_board = Board(title=escape(data['title']), author_id=user)
db.session.add(new_board)
db.session.commit()
return new_board.json(), 201
def post(id, user, token_info):
data = request.json
new_post = Post(message=escape(data['message']), author_id=user, board_id=id)
db.session.add(new_post)
db.session.commit()
return new_post.json(), 201
def all(page=1, size=10):
def posts(id, page=1, size=10):
board = Board.query.get(id)
if not board:
raise BoardNotFound(id)
data = Post.query.order_by(Post.created.desc()).paginate(page, size, False)
data = Post.query.filter_by(board_id=id).order_by(Post.created.desc()).paginate(page, size, False)
return parse_page(data)
def show_all(page=1, size=10):
data = Board.query.order_by(Board.created.desc()).paginate(page, size, False)
return parse_page(data)
......@@ -18,6 +18,7 @@ PUBLIC_KEY = b"""-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAza5xftTPKTpef9sXgaJD41veGVeSshAEdaK/iRHHVVOsgVxEGxQl6UfCe1CTUV8Xq4NLISk5chNdMHDJBOlyXIc0xuA3F48kEMRKEuVAug9AruEF4xTX87Ya3B5sL38nLF+SkExKLz+05C/JERAsHE00eP3+/UkRIx2Zv+hWC3+bMv9uK6QmGoCBy9829TzDsdRaPFzj9DTTT44K9Ko/KugffnB5kgI49SUSqo/hyohCEZN0Abbho9f38tXNV4d+9dantkbUCNNmj3zGiYbzxBjEokxIMUn2N/57ys9ZhNllLEy5eSAoOF6hp1I0vN8c9uGbAmA0h1TJutsP6f5tEQIDAQAB
-----END PUBLIC KEY-----"""
@cnx.app.errorhandler(ProjectException)
def exception_handler(e):
return {
......
POST_CODE_BASE = 100
USER_CODE_BASE = 200
UPLOAD_CODE_BASE = 300
BOARD_CODE_BASE = 400
class ProjectException(Exception):
......@@ -19,7 +24,21 @@ class PostNotFound(ProjectException):
return f'Post with id {self.post_id} doesn\'t exist'
def get_code(self):
return 100
return POST_CODE_BASE
def get_http_code(self):
return 404
class BoardNotFound(ProjectException):
def __init__(self, board_id):
self.board_id = board_id
def get_message(self):
return f'Board with id {self.board_id} doesn\'t exist'
def get_code(self):
return BOARD_CODE_BASE
def get_http_code(self):
return 404
......@@ -33,7 +52,7 @@ class UserNotFound(ProjectException):
return f"User with id {self.user_id} doesn't exist."
def get_code(self):
return 200
return USER_CODE_BASE
def get_http_code(self):
return 404
......@@ -47,32 +66,32 @@ class RegistrationFailed(ProjectException):
return f"Failed to register new user, reason: {self.reason}"
def get_code(self):
return 201
return USER_CODE_BASE + 1
def get_http_code(self):
return 422
class UnsupportedFileType(ProjectException):
def __init__(self, filetype):
self.filetype = filetype
class WrongCredentials(ProjectException):
def get_message(self):
return f"Files with the extension {self.filetype} aren't allowed."
return "The username or password you provided are incorrect."
def get_code(self):
return 300
return USER_CODE_BASE + 2
def get_http_code(self):
return 412
return 401
class WrongCredentials(ProjectException):
class UnsupportedFileType(ProjectException):
def __init__(self, filetype):
self.filetype = filetype
def get_message(self):
return "The username or password you provided are incorrect."
return f"Files with the extension {self.filetype} aren't allowed."
def get_code(self):
return 202
return UPLOAD_CODE_BASE
def get_http_code(self):
return 401
return 412
......@@ -3,12 +3,31 @@ from config import db
from uuid import uuid4
class Board(db.Model):
__tablename__ = 'boards'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(255), unique=True)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
posts = db.relationship("Post")
created = db.Column(db.DateTime, default=datetime.utcnow)
def json(self):
return {
'id': self.id,
'title': self.title,
'author': self.author.username,
'created': self.created
}
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
message = db.Column(db.Text, nullable=False)
author_id = db.Column(db.String(36), db.ForeignKey('users.id'))
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
board_id = db.Column(db.Integer, db.ForeignKey('boards.id'))
created = db.Column(db.DateTime, default=datetime.utcnow)
def json(self):
......@@ -34,6 +53,7 @@ class User(db.Model):
password = db.Column(db.String(60), nullable=False)
created = db.Column(db.DateTime, default=datetime.utcnow)
posts = db.relationship("Post", backref='author')
boards = db.relationship("Board", backref='author')
def json(self):
return {
......
......@@ -6,6 +6,7 @@ chardet==3.0.4
Click==7.0
clickclick==1.2.2
connexion==2.6.0
cryptography==2.8
Flask==1.1.1
Flask-SQLAlchemy==2.4.1
idna==2.8
......@@ -17,6 +18,8 @@ jsonschema==3.2.0
MarkupSafe==1.1.1
openapi-spec-validator==0.2.8
pycparser==2.19
PyJWT==1.7.1
pyOpenSSL==19.1.0
pyrsistent==0.15.7
PyYAML==5.3
requests==2.22.0
......@@ -26,4 +29,3 @@ swagger-ui-bundle==0.0.6
urllib3==1.25.8
Werkzeug==1.0.0
zipp==2.2.0
openapi: 3.0.2
info:
version: 1.0.0
version: 1.0.1
title: 'Chytro'
description: 'Aplikácia pre nástenku'
......@@ -119,10 +119,10 @@ paths:
404:
description: 'Post not found'
/board:
/board/{id}:
get:
description: 'Get all posts paged'
operationId: boards.all
operationId: boards.posts
tags:
- Boards
......@@ -131,14 +131,13 @@ paths:
parameters:
- $ref: '#/components/parameters/PageNumber'
- $ref: '#/components/parameters/PageSize'
- $ref: '#/components/parameters/id'
responses:
200:
description: 'Paged responses here...'
content:
application/json:
schema:
$ref: '#/components/schemas/PostPage'
$ref: '#/components/responses/PostPage'
404:
description: 'Board not found'
post:
description: 'Post a new Post'
......@@ -146,6 +145,9 @@ paths:
tags:
- Boards
parameters:
- $ref: '#/components/parameters/id'
requestBody:
required: true
content:
......@@ -156,11 +158,60 @@ paths:
message:
type: string
responses:
201:
$ref: '#/components/responses/PostDetail'
400:
description: 'Bad Request'
422:
description: 'Unprocessable Entity'
/boards:
get:
description: 'Get a list of boards'
operationId: boards.show_all
security: []
parameters:
- $ref: '#/components/parameters/PageNumber'
- $ref: '#/components/parameters/PageSize'
tags:
- Boards
responses:
200:
description: 'a list of all available boards.'
content:
application/json:
schema:
$ref: '#/components/schemas/BoardPage'
put:
description: 'Create a new Board resource'
operationId: boards.create
tags:
- Boards
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
title:
type: string
responses:
201:
$ref: '#/components/responses/PostDetail'
description: 'Board created'
content:
application/json:
schema:
$ref: '#/components/schemas/Board'
400:
description: 'Bad Request'
422:
......@@ -187,9 +238,24 @@ components:
schemas:
Board:
type: object
properties:
id:
type: integer
title:
type: string
author:
type: string
created:
type: string
Post:
type: object
properties:
id:
type: integer
message:
type: string
author:
......@@ -209,6 +275,8 @@ components:
User:
type: object
properties:
id:
type: integer
username:
type: string
created:
......@@ -231,6 +299,20 @@ components:
totalPages:
type: integer
BoardPage:
type: object
properties:
content:
type: array
items:
$ref: '#/components/schemas/Board'
page:
type: integer
size:
type: integer
totalPages:
type: integer
parameters:
id:
name: id
......@@ -255,6 +337,13 @@ components:
responses:
PostPage:
description: 'A page of posts details'
content:
application/json:
schema:
$ref: '#/components/schemas/PostPage'
PostDetail:
description: 'Returns a complete detail of a Post object.'
content:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment