zcash-grant-system/backend/grant/user/views.py

223 lines
7.2 KiB
Python
Raw Normal View History

2018-11-16 19:33:25 -08:00
from flask import Blueprint, g, request
2018-10-22 15:31:33 -07:00
from flask_yoloapi import endpoint, parameter
from grant.proposal.models import Proposal, proposal_team, ProposalTeamInvite, invites_with_proposal_schema
2018-11-07 11:19:12 -08:00
from grant.utils.auth import requires_sm, requires_same_user_auth, verify_signed_auth, BadSignatureException
2018-11-16 19:33:25 -08:00
from grant.utils.upload import save_avatar, send_upload, remove_avatar
from grant.settings import UPLOAD_URL
from .models import User, SocialMedia, Avatar, users_schema, user_schema, db
blueprint = Blueprint('user', __name__, url_prefix='/api/v1/users')
@blueprint.route("/", methods=["GET"])
2018-10-22 15:31:33 -07:00
@endpoint.api(
parameter('proposalId', type=str, required=False)
)
def get_users(proposal_id):
proposal = Proposal.query.filter_by(id=proposal_id).first()
if not proposal:
users = User.query.all()
else:
2018-11-13 08:07:09 -08:00
users = (
User.query
.join(proposal_team)
.join(Proposal)
.filter(proposal_team.c.proposal_id == proposal.id)
.all()
)
result = users_schema.dump(users)
2018-10-22 15:31:33 -07:00
return result
@blueprint.route("/me", methods=["GET"])
@requires_sm
@endpoint.api()
def get_me():
dumped_user = user_schema.dump(g.current_user)
return dumped_user
@blueprint.route("/<user_identity>", methods=["GET"])
@endpoint.api()
def get_user(user_identity):
2018-11-07 11:19:12 -08:00
user = User.get_by_identifier(email_address=user_identity, account_address=user_identity)
if user:
result = user_schema.dump(user)
return result
else:
message = "User with account_address or user_identity matching {} not found".format(user_identity)
return {"message": message}, 404
@blueprint.route("/", methods=["POST"])
2018-10-22 15:31:33 -07:00
@endpoint.api(
parameter('accountAddress', type=str, required=True),
parameter('emailAddress', type=str, required=True),
parameter('displayName', type=str, required=True),
parameter('title', type=str, required=True),
parameter('signedMessage', type=str, required=True),
parameter('rawTypedData', type=str, required=True)
2018-10-22 15:31:33 -07:00
)
def create_user(
account_address,
email_address,
display_name,
title,
signed_message,
raw_typed_data
):
2018-11-07 11:19:12 -08:00
existing_user = User.get_by_identifier(email_address=email_address, account_address=account_address)
if existing_user:
2018-10-22 15:31:33 -07:00
return {"message": "User with that address or email already exists"}, 409
# Handle signature
try:
sig_address = verify_signed_auth(signed_message, raw_typed_data)
if sig_address.lower() != account_address.lower():
return {
2018-11-16 19:33:25 -08:00
"message": "Message signature address ({sig_address}) doesn't match account_address ({account_address})".format(
sig_address=sig_address,
account_address=account_address
2018-11-16 19:33:25 -08:00
)
}, 400
except BadSignatureException:
return {"message": "Invalid message signature"}, 400
# TODO: Handle avatar & social stuff too
user = User.create(
account_address=account_address,
email_address=email_address,
display_name=display_name,
title=title
)
result = user_schema.dump(user)
2018-10-22 15:31:33 -07:00
return result
@blueprint.route("/auth", methods=["POST"])
@endpoint.api(
parameter('accountAddress', type=str, required=True),
parameter('signedMessage', type=str, required=True),
parameter('rawTypedData', type=str, required=True)
)
def auth_user(account_address, signed_message, raw_typed_data):
2018-11-07 11:19:12 -08:00
existing_user = User.get_by_identifier(account_address=account_address)
if not existing_user:
return {"message": "No user exists with that address"}, 400
try:
sig_address = verify_signed_auth(signed_message, raw_typed_data)
if sig_address.lower() != account_address.lower():
return {
2018-11-16 19:33:25 -08:00
"message": "Message signature address ({sig_address}) doesn't match account_address ({account_address})".format(
sig_address=sig_address,
account_address=account_address
2018-11-16 19:33:25 -08:00
)
}, 400
except BadSignatureException:
return {"message": "Invalid message signature"}, 400
return user_schema.dump(existing_user)
2018-11-16 19:33:25 -08:00
@blueprint.route("/avatar", methods=["POST"])
@requires_sm
@endpoint.api()
def upload_avatar():
user = g.current_user
if 'file' not in request.files:
return {"message": "No file in post"}, 400
file = request.files['file']
if file.filename == '':
return {"message": "No selected file"}, 400
try:
filename = save_avatar(file, user.id)
return {"url": "{0}/api/v1/users/avatar/{1}".format(UPLOAD_URL, filename)}
except Exception as e:
return {"message": str(e)}, 400
@blueprint.route("/avatar/<filename>", methods=["GET"])
def get_avatar(filename):
return send_upload(filename)
@blueprint.route("/avatar", methods=["DELETE"])
@requires_sm
@endpoint.api(
parameter('url', type=str, required=True)
)
def delete_avatar(url):
user = g.current_user
remove_avatar(url, user.id)
@blueprint.route("/<user_identity>", methods=["PUT"])
@requires_sm
2018-11-07 11:19:12 -08:00
@requires_same_user_auth
@endpoint.api(
parameter('displayName', type=str, required=True),
parameter('title', type=str, required=True),
parameter('socialMedias', type=list, required=True),
parameter('avatar', type=str, required=True)
)
def update_user(user_identity, display_name, title, social_medias, avatar):
user = g.current_user
if display_name is not None:
user.display_name = display_name
if title is not None:
user.title = title
db_socials = SocialMedia.query.filter_by(user_id=user.id).all()
for db_social in db_socials:
db.session.delete(db_social)
if social_medias is not None:
for social_media in social_medias:
sm = SocialMedia(social_media_link=social_media, user_id=user.id)
db.session.add(sm)
db_avatar = Avatar.query.filter_by(user_id=user.id).first()
if db_avatar:
db.session.delete(db_avatar)
if avatar:
new_avatar = Avatar(image_url=avatar, user_id=user.id)
db.session.add(new_avatar)
2018-11-26 17:14:00 -08:00
old_avatar_url = db_avatar and db_avatar.image_url
if old_avatar_url and old_avatar_url != new_avatar.image_url:
remove_avatar(old_avatar_url, user.id)
2018-11-16 19:33:25 -08:00
db.session.commit()
result = user_schema.dump(user)
return result
@blueprint.route("/<user_identity>/invites", methods=["GET"])
@requires_same_user_auth
@endpoint.api()
def get_user_invites(user_identity):
invites = ProposalTeamInvite.get_pending_for_user(g.current_user)
return invites_with_proposal_schema.dump(invites)
@blueprint.route("/<user_identity>/invites/<invite_id>/respond", methods=["PUT"])
@requires_same_user_auth
@endpoint.api(
parameter('response', type=bool, required=True)
)
def respond_to_invite(user_identity, invite_id, response):
invite = ProposalTeamInvite.query.filter_by(id=invite_id).first()
if not invite:
return {"message": "No invite found with id {}".format(invite_id)}, 404
invite.accepted = response
db.session.add(invite)
if invite.accepted:
invite.proposal.team.append(g.current_user)
db.session.add(invite)
db.session.commit()
return None, 200