From 9f4795b9f10597d1bf48bbffec871e3b258eb4ba Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Fri, 2 Jul 2021 16:26:36 +0200 Subject: [PATCH 1/3] add profile visible flag --- ki/handlers/update_profile.py | 1 + ki/models.py | 4 +++- ki/test/test_profile_endpoint.py | 3 +++ ...tial_migration.py => ebb2dd1fb371_initial_migration.py} | 7 ++++--- 4 files changed, 11 insertions(+), 4 deletions(-) rename migrations/versions/{f95308aceda1_initial_migration.py => ebb2dd1fb371_initial_migration.py} (96%) diff --git a/ki/handlers/update_profile.py b/ki/handlers/update_profile.py index d69ea5d..57bcc39 100644 --- a/ki/handlers/update_profile.py +++ b/ki/handlers/update_profile.py @@ -107,6 +107,7 @@ def update_profile(user_id: int): profile.pronouns = request.json.get("pronouns", "") profile.volunteerwork = request.json.get("volunteerwork", "") profile.freetext = request.json.get("freetext", "") + profile.visible = request.json.get("visible", False) update_address(profile, request.json.get("address", {})) update_contacts(profile, request.json.get("contacts", {})) diff --git a/ki/models.py b/ki/models.py index d15278e..26a4dae 100644 --- a/ki/models.py +++ b/ki/models.py @@ -1,6 +1,6 @@ from datetime import datetime -from sqlalchemy import Column, Integer, SmallInteger, String, DateTime, ForeignKey +from sqlalchemy import Boolean, Column, Integer, SmallInteger, String, DateTime, ForeignKey from sqlalchemy.orm import relationship from app import db @@ -28,6 +28,7 @@ class Profile(db.Model): pronouns = Column(String(25), default="") volunteerwork = Column(String(4000), default="") freetext = Column(String(4000), default="") + visible = Column(Boolean, nullable=False, default=False) created = Column(DateTime, nullable=False, default=datetime.now) updated = Column(DateTime, onupdate=datetime.now, nullable=False, default=datetime.now) @@ -44,6 +45,7 @@ class Profile(db.Model): "pronouns": self.pronouns, "volunteerwork": self.volunteerwork, "freetext": self.freetext, + "visible": self.visible, "address": self.address.to_dict(), "contacts": list(map(lambda contact: contact.to_dict(), self.contacts)), "skills": list(map(lambda skill: skill.to_dict(), self.skills)), diff --git a/ki/test/test_profile_endpoint.py b/ki/test/test_profile_endpoint.py index 0caa6c4..626d104 100644 --- a/ki/test/test_profile_endpoint.py +++ b/ki/test/test_profile_endpoint.py @@ -30,6 +30,7 @@ class TestProfileEndpoint(ApiTest): "pronouns": "Monsieur", "volunteerwork": "ja", "freetext": "Hallo", + "visible": True, "address": { "name": "Peeeda", "street": "Bachstraße", @@ -92,6 +93,7 @@ class TestProfileEndpoint(ApiTest): self.assertEqual("Monsieur", profile.pronouns) self.assertEqual("ja", profile.volunteerwork) self.assertEqual("Hallo", profile.freetext) + self.assertTrue(profile.visible) address = profile.address self.assertEqual(address.name, "Peeeda") @@ -156,6 +158,7 @@ class TestProfileEndpoint(ApiTest): "pronouns": "Herr Dr. Dr.", "freetext": "Ich mag Kaffee", "volunteerwork": "Gartenverein", + "visible": False, "address": { "additional": "Hinterhaus", "city": "Bielefeld", diff --git a/migrations/versions/f95308aceda1_initial_migration.py b/migrations/versions/ebb2dd1fb371_initial_migration.py similarity index 96% rename from migrations/versions/f95308aceda1_initial_migration.py rename to migrations/versions/ebb2dd1fb371_initial_migration.py index 8aa7429..540afcd 100644 --- a/migrations/versions/f95308aceda1_initial_migration.py +++ b/migrations/versions/ebb2dd1fb371_initial_migration.py @@ -1,8 +1,8 @@ """Initial migration. -Revision ID: f95308aceda1 +Revision ID: ebb2dd1fb371 Revises: -Create Date: 2021-06-20 19:11:47.086814 +Create Date: 2021-07-02 16:20:18.160228 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'f95308aceda1' +revision = 'ebb2dd1fb371' down_revision = None branch_labels = None depends_on = None @@ -34,6 +34,7 @@ def upgrade(): sa.Column('pronouns', sa.String(length=25), nullable=True), sa.Column('volunteerwork', sa.String(length=4000), nullable=True), sa.Column('freetext', sa.String(length=4000), nullable=True), + sa.Column('visible', sa.Boolean(), nullable=False), sa.Column('created', sa.DateTime(), nullable=False), sa.Column('updated', sa.DateTime(), nullable=False), sa.PrimaryKeyConstraint('id'), From 4ca8660b1d968eaf7bc5fe50aae433c00d39064f Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Fri, 2 Jul 2021 16:33:48 +0200 Subject: [PATCH 2/3] implement visible authorisation --- ki/routes.py | 3 +++ ki/test/test_profile_endpoint.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/ki/routes.py b/ki/routes.py index f9e700d..ac13884 100644 --- a/ki/routes.py +++ b/ki/routes.py @@ -116,6 +116,9 @@ def get_user_profile(user_id): if profile is None: return make_response({}, 404) + if not profile.visible and profile.user.id != g.user.id: + return make_response({}, 403) + return make_response({ "profile": profile.to_dict(), }) diff --git a/ki/test/test_profile_endpoint.py b/ki/test/test_profile_endpoint.py index 626d104..33b786b 100644 --- a/ki/test/test_profile_endpoint.py +++ b/ki/test/test_profile_endpoint.py @@ -139,6 +139,11 @@ class TestProfileEndpoint(ApiTest): self.assertEqual(second_language.language_id, "es") self.assertEqual(second_language.level, 2) + def test_get_profile_unauthorised(self): + + response = self.client.get("/users/1/profile") + self.assertEqual(response.status_code, 401) + def test_get_profile(self): login_data = {"username": "peter", "password": "geheim"} login_response = self.client.post("/users/login", data=json.dumps(login_data), content_type="application/json") From f589ed063e73f693c55cd07b68fc38f318dce969 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Fri, 2 Jul 2021 16:49:14 +0200 Subject: [PATCH 3/3] add contacttypes seed and endpoint --- data/seed_data/contacttypes.csv | 10 +++++++ ki/actions/seed.py | 28 +++++++++++++------ ki/routes.py | 8 +++++- ki/test/test_contacttypes_endpoint.py | 40 +++++++++++++++++++++++++++ ki/test/test_profile_endpoint.py | 6 ++-- 5 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 data/seed_data/contacttypes.csv create mode 100644 ki/test/test_contacttypes_endpoint.py diff --git a/data/seed_data/contacttypes.csv b/data/seed_data/contacttypes.csv new file mode 100644 index 0000000..bfc5d83 --- /dev/null +++ b/data/seed_data/contacttypes.csv @@ -0,0 +1,10 @@ +id,name +1,E-Mail +2,Mobiltelefon +3,Festnetz +4,Matrix +5,Mastodon +6,Telegram +7,Jabber/XMPP +8,Web +9,Twitter diff --git a/ki/actions/seed.py b/ki/actions/seed.py index e889c89..272595c 100644 --- a/ki/actions/seed.py +++ b/ki/actions/seed.py @@ -5,7 +5,25 @@ from app import app, db from ki.models import Address, Contact, ContactType, Language, Skill, Profile, ProfileLanguage, ProfileSkill, User +def seed_contacttypes(): + contacttypes_seed_file_path = app.config["KI_DATA_DIR"] + "/seed_data/contacttypes.csv" + + logging.info("importing contacttypes") + + with open(contacttypes_seed_file_path) as file: + csv_reader = csv.DictReader(file) + + for contacttype in csv_reader: + id = int(contacttype["id"]) + db_contacttype = ContactType.query.get(id) + + if db_contacttype is None: + db.session.add(ContactType(id=int(contacttype["id"]), name=contacttype["name"])) + + def seed(dev: bool): + seed_contacttypes() + skill_seed_file_path = app.config["KI_DATA_DIR"] + "/seed_data/skills.csv" logging.info("importing skills") @@ -47,16 +65,10 @@ def seed(dev: bool): user=peter) db.session.add(peters_profile) - matrix_type = ContactType(name="Matrix") - db.session.add(matrix_type) - - matrix_contact = Contact(profile=peters_profile, contacttype=matrix_type, content="@peter:wtf-eg.de") + matrix_contact = Contact(profile=peters_profile, contacttype_id=4, content="@peter:wtf-eg.de") db.session.add(matrix_contact) - email_type = ContactType(name="E-Mail") - db.session.add(email_type) - - email_contact = Contact(profile=peters_profile, contacttype=email_type, content="peter@wtf-eg.de") + email_contact = Contact(profile=peters_profile, contacttype_id=1, content="peter@wtf-eg.de") db.session.add(email_contact) peters_address = Address(name="Peter Nichtlustig", diff --git a/ki/routes.py b/ki/routes.py index ac13884..51cec70 100644 --- a/ki/routes.py +++ b/ki/routes.py @@ -4,7 +4,7 @@ from functools import wraps from ki.auth import auth from ki.handlers import update_profile as update_profile_handler -from ki.models import Language, Skill, Token, User +from ki.models import ContactType, Language, Skill, Token, User from app import app @@ -133,6 +133,12 @@ def update_profile(user_id): return update_profile_handler(int(user_id)) +@app.route("/contacttypes") +@token_auth +def get_contacttypes(): + return handle_completion_request(ContactType, "contacttypes") + + @app.route("/skills") @token_auth def get_skills(): diff --git a/ki/test/test_contacttypes_endpoint.py b/ki/test/test_contacttypes_endpoint.py new file mode 100644 index 0000000..be79d14 --- /dev/null +++ b/ki/test/test_contacttypes_endpoint.py @@ -0,0 +1,40 @@ +import unittest + +from ki.test.ApiTest import ApiTest + + +class TestContactTypesEndpoint(ApiTest): + def test_skills_options(self): + response = self.client.options("/contacttypes") + self.assertEqual(response.status_code, 200) + self.assertIn("Access-Control-Allow-Origin", response.headers) + self.assertEqual(response.headers["Access-Control-Allow-Origin"], "*") + + def test_get_contacttypes_unauthorised(self): + response = self.client.get("/contacttypes?search=m") + self.assertEqual(response.status_code, 401) + + def test_get_contacttypes(self): + token = self.login("peter", "geheim")["token"] + + response = self.client.get("/contacttypes?search=m", headers={"Authorization": "Bearer " + token}) + self.assertEqual(response.status_code, 200) + self.assertEqual( + { + "contacttypes": [{ + "id": 5, + "name": "Mastodon" + }, { + "id": 4, + "name": "Matrix" + }, { + "id": 2, + "name": "Mobiltelefon" + }] + }, response.json) + self.assertIn("Access-Control-Allow-Origin", response.headers) + self.assertEqual(response.headers["Access-Control-Allow-Origin"], "*") + + +if __name__ == "main": + unittest.main() diff --git a/ki/test/test_profile_endpoint.py b/ki/test/test_profile_endpoint.py index 33b786b..7ee583e 100644 --- a/ki/test/test_profile_endpoint.py +++ b/ki/test/test_profile_endpoint.py @@ -43,7 +43,7 @@ class TestProfileEndpoint(ApiTest): "contacts": [{ "id": 1, "contacttype": { - "id": 1, + "id": 4, "name": "Matrix" }, "content": "@peeda:wtf-eg.de" @@ -179,7 +179,7 @@ class TestProfileEndpoint(ApiTest): "id": 1, "profile_id": 1, "contacttype": { - "id": 1, + "id": 4, "name": "Matrix" }, "content": "@peter:wtf-eg.de" @@ -187,7 +187,7 @@ class TestProfileEndpoint(ApiTest): "id": 2, "profile_id": 1, "contacttype": { - "id": 2, + "id": 1, "name": "E-Mail" }, "content": "peter@wtf-eg.de"