async def create_group_invitation(req: request, user_id: int, group_id: int): """ POST /group/invitation/<group_id:int> creates a invitation code for a group :param req: :param user_id: :param group_id: :return: """ async with req.app.pool.acquire() as connection: async with connection.transaction(): code_exists = True while code_exists: invitation_code = uuid.uuid4().hex code_exists = len( await connection.fetch('SELECT id FROM group_invitations WHERE code=$1', invitation_code) ) > 0 try: expires_at = datetime.fromtimestamp(int(req.form['expires_at'][0])) if 'expires_at' in req.form else None max_uses = int(req.form['max_uses'][0]) if 'max_uses' in req.form else 0 except TypeError: return json_r({"error": "Invalid parameters"}, 400) except ValueError: return json_r({"error": "Timestamp must be a valid integer"}, 400) await connection.execute( 'INSERT INTO group_invitations(code, group_id, created_by, expires_at, max_uses) ' + 'VALUES($1,$2,$3,$4,$5)', invitation_code, group_id, user_id, expires_at, max_uses ) return json_r({"success": True, "code": invitation_code})
async def login_user(req: request): """ Tries to login an user, if success, return its JWT :param req: :return: """ if 'username' not in req.form or 'password' not in req.form: return json_r({"error": "Username or password not sent"}, 400) # Do login to the user async with req.app.pool.acquire() as connection: # Open a transaction. async with connection.transaction(): # Run the query passing the request argument. user_search = await connection.fetch( "SELECT * FROM users WHERE username=$1", req.form["username"][0] ) if len(user_search) == 0: return json_r({"error": "Invalid credentials"}, 400) user_password = user_search[0]["password"] if not pbk.verify(req.form["password"][0], user_password): return json_r({"error": "Invalid credentials"}, 400) token_payload = { "user": user_search[0]["id"], "expires_in": 7200, "expires_at": int(time.time()) + 7200 } return json_r({"token": jwt.encode(token_payload, req.app.app_config["key"], algorithm='HS256')})
async def register_user(req: request): """ Tries to register an user inside the app. As long as the username doesn't exists already :param req: :return: """ if 'username' not in req.form or 'password' not in req.form: return json_r({"error": "Username or password not sent"}, 400) async with req.app.pool.acquire() as connection: async with connection.transaction(): # Check if the user already exists user_search = await connection.fetch( "SELECT * FROM users WHERE username=$1", req.form["username"][0] ) # If exists, then return error if len(user_search) != 0: return json_r({"error": "Username already exists"}, 400) # Register the user await connection.execute( "INSERT INTO users(username, password) VALUES($1,$2)", req.form["username"][0], pbk.hash(req.form["password"][0]) ) return json_r({"success": True})
async def quit_from_group(req: request, user_id: int, group_id: int): """ POST /group/exit/<group_id:int> Exits from a group :param req: :param user_id: :param group_id: :return: """ async with req.app.pool.acquire() as connection: async with connection.transaction(): group_info = await connection.fetch( 'SElECT id FROM groups WHERE id=$1 AND created_by=$2', group_id, user_id ) if len(group_info) > 0: # Is the owner of the group, so no return json_r({"error": "You are the owner of this group, delete it instead"}, 400) # Delete from the group await connection.execute('DELETE FROM belongs_to_group WHERE group_id=$1 AND user_id=$2', group_id, user_id) return json_r({"success": True}, 200)
async def is_logged(req: request, *args, **kwargs): if not req.headers.get("dango-key"): return json_r({"error": "Unauthorized"}, 401) # Get the token owner owner = await auth_helpers.token_owner(req.headers.get("dango-key"), req) # invalid owner or expired token if owner == -1: return json_r({"error": "Unauthorized"}, 401) # Check all the permissions for permission in permissions: if permission in auth_helpers.permission_check: check = await auth_helpers.permission_check[permission](owner, req, *args, **kwargs) if not check: return json_r({"error": "Unauthorized"}, 401) else: print(f'Permission check for {permission} not implemented') # Execute the function if send_user_id: resp = await fn(req, owner, *args, **kwargs) else: resp = await fn(req, *args, **kwargs) return resp
async def user_info(req: request, user_id: int): async with req.app.pool.acquire() as connection: async with connection.transaction(): # Check if the user already exists user_search = await connection.fetch( "SELECT id, username FROM users WHERE id=$1", user_id ) if len(user_search) == 0: return json_r({"user": {}}) user_details = dict(user_search[0]) return json_r({"user": user_details})
async def get_user_groups(req: request, user_id: int): """ Get all groups :param req: :param user_id: :return: """ async with req.app.pool.acquire() as connection: async with connection.transaction(): order_by, order_type = get_sorting_params(req) limit, page = get_limit_and_page(req) groups = await connection.fetch( f'SELECT g.group_name, g.created_at, g.updated_at, u.username from (SELECT * FROM groups INNER JOIN belongs_to_group ON groups.id=belongs_to_group.group_id AND belongs_to_group.user_id=$1) as g INNER JOIN users AS u ON u.id=g.created_by ORDER BY {order_by} {order_type} LIMIT $2 OFFSET $3', user_id, limit, (page - 1) * limit ) total_groups = (await connection.fetch( 'SELECT count(group_id) FROM belongs_to_group WHERE user_id=$1', user_id ))[0]['count'] return json_r({ "total": total_groups, "items_per_page": limit, "groups": [dict(g) for g in groups] })
async def get_group_participants(req: request): """ Get all persons that belongs to a group :param req: :return: """ async with req.app.pool.acquire() as connection: async with connection.transaction(): group_id = int(req.args['group-id'][0]) limit, page = get_limit_and_page(req) users = await connection.fetch( 'SELECT users.username from belongs_to_group INNER JOIN users ON users.id=belongs_to_group.user_id WHERE group_id=$1 ORDER BY username asc LIMIT $2 OFFSET $3', group_id, limit, (page-1)*limit ) total_users = (await connection.fetch( 'SELECT count(user_id) FROM belongs_to_group WHERE group_id=$1', group_id ))[0]['count'] return json_r({ "total": total_users, "items_per_page": limit, "users": [dict(u) for u in users] })
async def get_user_group_invitations(req: request, user_id: int): """ GET /group/invitation?group-id=<id> :param req: :param user_id: :param group_id: :return: """ async with req.app.pool.acquire() as connection: async with connection.transaction(): invitations = await connection.fetch( "SELECT * FROM group_invitations WHERE group_id=$1 AND created_by=$2", int(req.args['group-id'][0]), user_id ) user_invitations = {"invitations": [*map( lambda invitation: { "code": invitation["code"], "max_uses":invitation["max_uses"], "current_uses": invitation["current_uses"], "expires_at": invitation["expires_at"] }, invitations )]} return json_r(user_invitations, 200)
async def is_logged(req: request, *args, **kwargs): if not req.headers.get("dango-key"): return json_r({"error": "Unauthorized"}, 401) # Get the token owner owner = await auth_helpers.token_owner(req.headers.get("dango-key"), req) # invalid owner or expired token if owner == -1: return json_r({"error": "Unauthorized"}, 401) # Execute the function if send_user_id: resp = await fn(req, owner, *args, **kwargs) else: resp = await fn(req, *args, **kwargs) return resp
async def user_info_detailed(req: request, user_id: int): """ Get the details of an specified user :param req: :param user_id: :return: """ # TODO - Shares at least one group with the user async with req.app.pool.acquire() as connection: async with connection.transaction(): # Check if the user already exists user_search = await connection.fetch( "SELECT id, username FROM users WHERE id=$1", user_id ) if len(user_search) == 0: return json_r({"user": {}}) user_details = dict(user_search[0]) return json_r({"user": user_details})
async def delete_group(req: request, user_id: int, group_id: int): """ DELETE /group/<group_id:int> Deletes a group and all users belonging to it :param req: :param user_id: :param group_id: :return: """ async with req.app.pool.acquire() as connection: async with connection.transaction(): await connection.execute('DELETE FROM groups WHERE id=$1', group_id) return json_r({"success": True})
async def create_group(req: request, user_id: int): """ POST /group Creates a new group for the desired user :param req: :param user_id: :return: """ if 'name' not in req.form or len(req.form['name']) != 1: return json_r({"error": "Invalid parameters"}, 400) name = req.form['name'][0] if not isinstance(name, str): return json_r({"error": "Invalid parameters"}, 400) async with req.app.pool.acquire() as connection: async with connection.transaction(): group_search = await connection.fetch( "SELECT * FROM groups WHERE created_by=$1 AND group_name=$2", user_id, name ) if len(group_search) > 0: return json_r({"error": "You have already created a group with that name"}, 400) group_insert = await connection.fetch( "INSERT INTO groups(created_by, group_name) VALUES($1,$2) RETURNING id", user_id, name ) group_id = group_insert[0]["id"] # insert into reference, so its known that the user belongs to that group await connection.execute( "INSERT INTO belongs_to_group(user_id, group_id) VALUES ($1, $2)", user_id, group_id ) return json_r({"success": True, "id": group_id})
async def join_to_group(req: request, user_id: int): """ POST /group/join Joins to a group. The invite of the group is required :param req: :param user_id: :return: """ if 'code' not in req.form or len(req.form['code']) > 1: return json_r({"error": "Invalid parameters"}, 400) async with req.app.pool.acquire() as connection: async with connection.transaction(): code = req.form['code'][0] invitation_info = await connection.fetch( 'SELECT * FROM group_invitations WHERE code=$1', code ) if len(invitation_info) == 0: # Invalid code return json_r({"error": "Invalid invitation"}, 400) max_uses = int(invitation_info[0]['max_uses']) current_uses = int(invitation_info[0]['current_uses']) group_id = int(invitation_info[0]['group_id']) if invitation_info[0]['expires_at'] is not None and int(time.time()) > int(invitation_info[0]['expires_at']): # Expired invitation return json_r({"error": "Expired invitation"}, 400) elif max_uses != 0 and max_uses <= current_uses: # All invitation uses consumed return json_r({"error": "Expired invitation"}, 400) # Check if the user already belongs to that group already_belongs = len(await connection.fetch( "SELECT * FROM belongs_to_group WHERE user_id=$1 AND group_id=$2", user_id, group_id )) > 0 if already_belongs: return json_r({"error": "You already belong to that group"}, 400) # Not inside the group, so let's join in await connection.execute('INSERT INTO belongs_to_group(group_id, user_id) VALUES($1,$2)', group_id, user_id) if max_uses != 0: # increment the code if necessary await connection.execute('UPDATE group_invitations SET current_uses=current_uses+1 WHERE code=$1', code) # Get the group info so we can return it group_info = (await connection.fetch('SElECT id, group_name, created_at FROM groups WHERE id=$1', group_id))[0] return json_r({"success": True, "group": group_info})