mirror of
https://github.com/spiral-project/ihatemoney.git
synced 2025-04-28 17:32:38 +02:00
193 lines
5.7 KiB
Python
193 lines
5.7 KiB
Python
from functools import wraps
|
|
|
|
from flask import current_app, request
|
|
from flask_restful import Resource, abort
|
|
from werkzeug.security import check_password_hash
|
|
from wtforms.fields.core import BooleanField
|
|
|
|
from ihatemoney.forms import EditProjectForm, MemberForm, ProjectForm, get_billform_for
|
|
from ihatemoney.models import Bill, Person, Project, db
|
|
|
|
|
|
def need_auth(f):
|
|
"""Check the request for basic authentication for a given project.
|
|
|
|
Return the project if the authorization is good, abort the request with a 401 otherwise
|
|
"""
|
|
|
|
@wraps(f)
|
|
def wrapper(*args, **kwargs):
|
|
auth = request.authorization
|
|
project_id = kwargs.get("project_id").lower()
|
|
|
|
# Use Basic Auth
|
|
if auth and project_id and auth.username.lower() == project_id:
|
|
project = Project.query.get(auth.username.lower())
|
|
if project and check_password_hash(project.password, auth.password):
|
|
# The whole project object will be passed instead of project_id
|
|
kwargs.pop("project_id")
|
|
return f(*args, project=project, **kwargs)
|
|
else:
|
|
# Use Bearer token Auth
|
|
auth_header = request.headers.get("Authorization", "")
|
|
auth_token = ""
|
|
try:
|
|
auth_token = auth_header.split(" ")[1]
|
|
except IndexError:
|
|
abort(401)
|
|
project_id = Project.verify_token(
|
|
auth_token, token_type="auth", project_id=project_id
|
|
)
|
|
if auth_token and project_id:
|
|
project = Project.query.get(project_id)
|
|
if project:
|
|
kwargs.pop("project_id")
|
|
return f(*args, project=project, **kwargs)
|
|
abort(401)
|
|
|
|
return wrapper
|
|
|
|
|
|
class ProjectsHandler(Resource):
|
|
def post(self):
|
|
form = ProjectForm(meta={"csrf": False})
|
|
if form.validate() and current_app.config.get("ALLOW_PUBLIC_PROJECT_CREATION"):
|
|
project = form.save()
|
|
db.session.add(project)
|
|
db.session.commit()
|
|
return project.id, 201
|
|
return form.errors, 400
|
|
|
|
|
|
class ProjectHandler(Resource):
|
|
method_decorators = [need_auth]
|
|
|
|
def get(self, project):
|
|
return project
|
|
|
|
def delete(self, project):
|
|
db.session.delete(project)
|
|
db.session.commit()
|
|
return "DELETED"
|
|
|
|
def put(self, project):
|
|
form = EditProjectForm(id=project.id, meta={"csrf": False})
|
|
if form.validate() and current_app.config.get("ALLOW_PUBLIC_PROJECT_CREATION"):
|
|
form.update(project)
|
|
db.session.commit()
|
|
return "UPDATED"
|
|
return form.errors, 400
|
|
|
|
|
|
class ProjectStatsHandler(Resource):
|
|
method_decorators = [need_auth]
|
|
|
|
def get(self, project):
|
|
return project.members_stats
|
|
|
|
|
|
class APIMemberForm(MemberForm):
|
|
"""Member is not disablable via a Form.
|
|
|
|
But we want Member.enabled to be togglable via the API.
|
|
"""
|
|
|
|
activated = BooleanField(false_values=("false", "", "False"))
|
|
|
|
def save(self, project, person):
|
|
person.activated = self.activated.data
|
|
return super(APIMemberForm, self).save(project, person)
|
|
|
|
|
|
class MembersHandler(Resource):
|
|
method_decorators = [need_auth]
|
|
|
|
def get(self, project):
|
|
return project.members
|
|
|
|
def post(self, project):
|
|
form = MemberForm(project, meta={"csrf": False})
|
|
if form.validate():
|
|
member = Person()
|
|
form.save(project, member)
|
|
db.session.commit()
|
|
return member.id, 201
|
|
return form.errors, 400
|
|
|
|
|
|
class MemberHandler(Resource):
|
|
method_decorators = [need_auth]
|
|
|
|
def get(self, project, member_id):
|
|
member = Person.query.get(member_id, project)
|
|
if not member or member.project != project:
|
|
return "Not Found", 404
|
|
return member
|
|
|
|
def put(self, project, member_id):
|
|
form = APIMemberForm(project, meta={"csrf": False}, edit=True)
|
|
if form.validate():
|
|
member = Person.query.get(member_id, project)
|
|
form.save(project, member)
|
|
db.session.commit()
|
|
return member
|
|
return form.errors, 400
|
|
|
|
def delete(self, project, member_id):
|
|
if project.remove_member(member_id):
|
|
return "OK"
|
|
return "Not Found", 404
|
|
|
|
|
|
class BillsHandler(Resource):
|
|
method_decorators = [need_auth]
|
|
|
|
def get(self, project):
|
|
return project.get_bills().all()
|
|
|
|
def post(self, project):
|
|
form = get_billform_for(project, True, meta={"csrf": False})
|
|
if form.validate():
|
|
bill = Bill()
|
|
form.save(bill, project)
|
|
db.session.add(bill)
|
|
db.session.commit()
|
|
return bill.id, 201
|
|
return form.errors, 400
|
|
|
|
|
|
class BillHandler(Resource):
|
|
method_decorators = [need_auth]
|
|
|
|
def get(self, project, bill_id):
|
|
bill = Bill.query.get(project, bill_id)
|
|
if not bill:
|
|
return "Not Found", 404
|
|
return bill, 200
|
|
|
|
def put(self, project, bill_id):
|
|
form = get_billform_for(project, True, meta={"csrf": False})
|
|
if form.validate():
|
|
bill = Bill.query.get(project, bill_id)
|
|
form.save(bill, project)
|
|
db.session.commit()
|
|
return bill.id, 200
|
|
return form.errors, 400
|
|
|
|
def delete(self, project, bill_id):
|
|
bill = Bill.query.delete(project, bill_id)
|
|
db.session.commit()
|
|
if not bill:
|
|
return "Not Found", 404
|
|
return "OK", 200
|
|
|
|
|
|
class TokenHandler(Resource):
|
|
method_decorators = [need_auth]
|
|
|
|
def get(self, project):
|
|
if not project:
|
|
return "Not Found", 404
|
|
|
|
token = project.generate_token()
|
|
return {"token": token}, 200
|