Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions backend/database/Federation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import sqlalchemy as sql
from web import sql_database as db
from sqlalchemy.orm import relationship
import util


class Federation(db.Model):
id = sql.Column(sql.Integer, primary_key=True)
name = sql.Column(sql.String(100), nullable=False)
domain = sql.Column(sql.String(200), nullable=False)
remote_password = sql.Column(sql.String(100), nullable=True)
remote_user_id = sql.Column(sql.Integer, nullable=True)
federation_user_id = sql.Column(sql.Integer, sql.ForeignKey(
'member.id', ondelete='CASCADE'), nullable=True)
federation_user = relationship(
'database.Member.Member', lazy="joined")
accepted = sql.Column(sql.Boolean, nullable=False, default=False)
initiator = sql.Column(sql.Boolean, nullable=False, default=False)

def to_dict(self):
return {
"id": self.id,
"name": self.name,
"domain": self.domain,
"remote_user_id": self.remote_user_id,
"federation_user_id": self.federation_user_id,
"accepted": self.accepted,
"initiator": self.initiator
}
1 change: 1 addition & 0 deletions backend/database/Models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
from database.KeyValue import KeyValue
from database.Reminder import Reminder
from database.Session import Session
from database.Federation import Federation
49 changes: 49 additions & 0 deletions backend/database/Queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,55 @@ def delete_transaction(self, transaction_id):
session.commit()
return True

def get_federation(self, federation_id):
with self.get_session() as session:
federation: Federation = session.query(
Federation).filter_by(id=federation_id).first()
return federation.to_dict()

def accept_federation(self, federation_id=None, user_id, remote_user_id=None, remote_password=None):
with self.get_session() as session:

if federation_id is not None:
federation: Federation = session.query(Federation).filter_by(id=federation_id).first()
federation.accepted = True
federation.federation_user_id = user_id
session.commit()
return federation.to_dict()
else if remote_user_id is not None and remote_password is not None:
federation: Federation = session.query(Federation).filter_by(federation_user_id=user_id).first()
federation.accept_federation = True
federation.remote_user_id = remote_user_id
federation.remote_password = remote_password
session.commit()
return federation.to_dict()

def get_federations(self):
with self.get_session() as session:
federations = session.query(
Federation).order_by(desc(Federation.id)).all()
output = []
for f in federations:
federation: Federation = f
output.append(federation.to_dict())

return output

def add_federation(self, name, domain, user_id=None, remote_password=None, remote_user_id=None):
with self.get_session() as session:
if user_id is not None:
# local initiator
new_federation = Federation(name=name, domain=domain, federation_user_id=user_id, initiator=True)
session.add(new_federation)
session.commit()
return new_federation.to_dict()
elif remote_password is not None and remote_user_id is not None:
# remote initiator
new_federation = Federation(name=name, domain=domain, remote_password=remote_password, remote_user_id=remote_user_id, initiator=False)
session.add(new_federation)
session.commit()
return new_federation.to_dict()

def delete_user(self, member_id):
with self.get_session() as session:
session.delete(session.query(
Expand Down
121 changes: 120 additions & 1 deletion backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import flask
import mail
import secrets
import requests

api_bp = flask.Blueprint("api", __name__, url_prefix="/api/")
api = Api(api_bp, doc='/docu/', base_url='/api')
Expand Down Expand Up @@ -94,7 +95,6 @@ def is_self_or_admin(request, member_id):
'amount': fields.Float(required=True)
})


@api.route('/users')
class GET_USERS(Resource):
@authenticated
Expand Down Expand Up @@ -962,6 +962,125 @@ def post(self):
"Logout", f"MemberID: {request.cookies.get(f'{util.auth_cookie_memberID}memberID')}")
return util.build_response("OK")

model_federation_local = api.model('Federation-Local', {
'domain': fields.String(required=True),
'name': fields.String(required=True),
})

model_federation_remote = api.model('Federation-Extern', {
'name': fields.String(required=True),
'domain': fields.String(required=True),
'password': fields.String(required=True),
'userID': fields.Integer(required=True),
})

model_federation_local_accept = api.model('Federation-Local-Accept', {
"federationID": fields.String(required=True)
})

model_federation_remote_accept = api.model('Federation-Local-Accept', {
"userID": fields.Integer(required=True),
"password": fields.String(required=True)
})

@api.route('/federations')
class get_federations(Resource):
def get(self):
"""
Get all federations
"""
return util.build_response(db.get_federations())

@api.route('/federation/local')
class add_federation_local(Resource):
@api.doc(body=model_federation_local)
def post(self):
"""
Create a federation request
"""

password = secrets.token_urlsafe(64)
member = db.add_user(request.json["name"], 0, password, alias=request.json["name"], hidden=True)
db.add_federation(request.json["name"], request.json["domain"], member["id"])

url = f"http://{request.json['domain']}/api/federation/remote"
payload = json.dumps({
"password": password,
"userID": member["id"]
})
headers = {
'Content-Type': 'application/json'
}

response = requests.request("POST", url, headers=headers, data=payload, timeout=5)
if (response.status_code != 200):
return util.build_response("Federation request failed")

responseJson = response.json()
if not ("status" in responseJson and responseJson["status"] == "ok"):
return util.build_response("Federation request failed")

return util.build_response("Federation request accepted")

@api.route('/federation/local/accept')
class accept_federation_local(Resource):
@api.doc(body=model_federation_local_accept)
def post(self):
"""
Accept a federation request
"""
remote = db.get_federation(request.json["federationID"])

password = secrets.token_urlsafe(64)

url = f"http://{remote['domain']}/api/federation/remote"
payload = json.dumps({
"name": util.federation_name,
"domain": util.domain,
"password": password,
"userID": member["id"]
})
headers = {
'Content-Type': 'application/json'
}

response = requests.request("POST", url, headers=headers, data=payload, timeout=5)
Comment thread Fixed
if (response.status_code != 200):
return util.build_response("Federation request failed")

responseJson = response.json()
if not ("status" in responseJson and responseJson["status"] == "ok"):
return util.build_response("Federation request failed")

member = db.add_user(remote["name"], 0, password, alias=remote["name"] hidden=True)
db.accept_federation(federation_id=remote["id"], user_id=member["id"])

@api.route('/federation/remote/accept')
class accept_federation_remote(Resource):
@api.doc(body=model_federation_remote_accept)
def post(self):
"""
Receive accept federation request
"""
local_user_id = 1
remote_user_id = requests.json["userID"]
remote_password = requests.json["password"]

db.accept_federation(user_id=local_user_id, remote_user_id=remote_user_id, remote_password=remote_password)

return util.build_response({"status": "ok"})

@api.route('/federation/remote')
class add_federation_remote(Resource):
@api.doc(body=model_federation_remote)
def post(self):
"""
Create a federation request
"""
db.add_federation(name=request.json["name"], domain=request.json["domain"], remote_password=request.json["password"], remote_user_id=request.json["userID"])

return util.build_response({"status": "ok"})


if __name__ == "__main__":
if util.logging_enabled:
Expand Down
1 change: 1 addition & 0 deletions backend/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"DOMAIN") else "127.0.0.1:3000"
logging_enabled = os.environ.get(
"DEBUG") == "true" if os.environ.get("DEBUG") else False
federation_name = os.environ.get("FEDERATION") if os.environ.get("FEDERATION") else domain

token = os.environ.get("X_AUTH_TOKEN")
old_domain = os.environ.get("OLD_DOMAIN")
Expand Down
Loading
Loading