From 8a9cc2bc472e9aded9bd244904a166608007d971 Mon Sep 17 00:00:00 2001 From: Mads Nylund <73914541+MadsNyl@users.noreply.github.com> Date: Sun, 4 Aug 2024 23:04:24 +0200 Subject: [PATCH] Early August Update (#816) * Feat(kontres)/add image to bookable item (#785) * added optional image to bookable item model * added update method in serializer to handle new images * linting * remove update method for images * Feat(kontres)/add approved by (#786) * added approved by field * endpoint will now set approved by * serializer will return full user object in approved_by_detail * created test for approved by * migration * remove unnecessary code * removed write-only field in approved-by context * Create minutes for Codex (#787) * init * format * Feat(minute)/viewset (#788) * added richer reponse on post and put * added to admin panel * added filter for minute * Feat(kontres)/add notification (#790) * created methods for sending notification to admin and user * endpoint will now send notification if needed * add migrations for new notification types * Memberships with fines activated (#791) init * Feat(user)/user bio (#758) * Created model, serializer and view for user-bio * Created user bio model and made migrations * Created user bio serializer + viewsets + added new endpoint * Tested create method + added bio serializer to user serializer * Format * Created update method and started testing * Debugging test failures in user retrieve * fixed model error * Created user_bio_factory + started testing put method * Created fixture for UserBio * Created custom excpetion for duplicate user bio * Added permissions and inherited from BaseModel * Modularized serializer for bio * Use correct serializers in viewset + added destroy method * Finished testing bio viewset integration + format * Changed environent file to .env to avoid pushing up keys * Fix: Flipped assertion statement in test, since user bio should not be deleted * skiped buggy test from kontres * added mark to pytest.skip * Moved keys to .env file and reverted docker variables * Skip buggy kontres test * format * Added str method to user_bio * Removed unused imports * format * Changed user relation to a OneToOne-field (same affect as ForeignKey(unique=True) + removed check for duplicate bio in serializer * Migrations + changed assertion status code in duplicate bio test (could try catch in serializer to produce 400 status code) * format * format * Changed limit for description 50 -> 500 + migrations * Migrate * added id to serializer * merged leaf nodes in migrations * format --------- Co-authored-by: Ester2109 <126612066+Ester2109@users.noreply.github.com> Co-authored-by: Mads Nylund Co-authored-by: Mads Nylund <73914541+MadsNyl@users.noreply.github.com> Co-authored-by: Tam Le * Update CHANGELOG.md * added filter for allowed photos for user (#794) added filter for allowed photos * Upped payment time when coming from waiting list (#796) * fixed paymenttime saved to db (#798) * fixed bug (#800) * Disallow users to unregister when payment is done (#802) added 400 status code for deleting paid registration * update changelog * Added serializer for category in event (#804) added serializer for category in event * Permission middelware (#806) * added a check for existing user and id on request * format * Permission refactor of QR Codes (#807) * added permissions to qr code and refactored viewset * format * removed unused imports * Permissions for payment orders (#808) * added read permissions * added permissions for payment order and tests * format * chore(iac): updated docs and force https (#810) chore: updated docs and force https * feat(iac): add terraform guardrails so index don't nuke our infra (#811) feat: add guardrails so index don't fup * Automatic registration for new users with Feide (#809) * started on feide registration endpoint * made endpoint for creating user with Feide * added test for parse group * finished * format * removes three years if in digtrans * changelog update * Feide env variables Terraform (#814) added feid env variables * added delete endpoint for file (#815) * added delete endpoint for file * Trigger Build * changed workflow to checkout v4 * changed from docker-compose to docker compose * Update CHANGELOG.md * format * format --------- Co-authored-by: Erik Skjellevik <98759397+eriskjel@users.noreply.github.com> Co-authored-by: haruixu <114171733+haruixu@users.noreply.github.com> Co-authored-by: Ester2109 <126612066+Ester2109@users.noreply.github.com> Co-authored-by: Tam Le Co-authored-by: martcl --- .github/workflows/ci.yaml | 24 ++++++++++---------- .gitignore | 1 + .terraform.lock.hcl | 2 ++ CHANGELOG.md | 1 + app/common/file_handler.py | 2 +- app/content/serializers/user.py | 20 ++++++++--------- app/content/urls.py | 4 +++- app/content/util/feide_utils.py | 27 +++++++++++----------- app/content/views/__init__.py | 2 +- app/content/views/feide.py | 7 ++++-- app/content/views/upload.py | 22 ++++++++++++++++++ infrastructure/containers.tf | 40 +++++++++++++++++++++++++++++++++ infrastructure/inputs.tf | 24 ++++++++++++++++++++ main.tf | 6 +++++ 14 files changed, 140 insertions(+), 42 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index cab568a1b..f6babc08e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout Code Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Python 3.9 uses: actions/setup-python@v2 @@ -35,7 +35,7 @@ jobs: steps: - name: Checkout Code Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up .env file run: | @@ -44,35 +44,35 @@ jobs: echo "VIPPS_MERCHANT_SERIAL_NUMBER=${{ secrets.VIPPS_MERCHANT_SERIAL_NUMBER }}" >> .env - name: Build the Stack - run: docker-compose build + run: docker compose build - name: Run the Stack - run: docker-compose up -d + run: docker compose up -d - name: Make DB Migrations - run: docker-compose run --rm web python manage.py migrate + run: docker compose run --rm web python manage.py migrate - name: Run Django Tests - run: docker-compose run --rm web pytest --cov=app + run: docker compose run --rm web pytest --cov=app - name: Tear down the Stack - run: docker-compose down + run: docker compose down check-migrations: runs-on: ubuntu-latest steps: - name: Checkout Code Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Build the Stack - run: docker-compose build + run: docker compose build - name: Run the Stack - run: docker-compose up -d + run: docker compose up -d - name: Check for unstaged migrations - run: docker-compose run --rm web python manage.py makemigrations --check --no-input + run: docker compose run --rm web python manage.py makemigrations --check --no-input - name: Tear down the Stack - run: docker-compose down \ No newline at end of file + run: docker compose down \ No newline at end of file diff --git a/.gitignore b/.gitignore index 05184f482..f1a7cc858 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ celerybeat-schedule .terraform *.tfvars +.terraform.lock.hcl \ No newline at end of file diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl index 3e007d98d..59f929fd4 100644 --- a/.terraform.lock.hcl +++ b/.terraform.lock.hcl @@ -5,6 +5,7 @@ provider "registry.terraform.io/hashicorp/azurerm" { version = "3.76.0" constraints = "> 3.68.0" hashes = [ + "h1:b7wCNsV0HyJalcmjth7Y4nSBuZqEjbA0Phpggoy4bLE=", "h1:eArCWwNEShXmVWS08Ocd3d8ptsjbAaMECifkIBacpyw=", "zh:33c6b1559b012d03befeb8ee9cf5b88c31acd64983dd4f727a49a436008b5577", "zh:36d3cfa7cf2079a102ffce05da2de41ecf263310544990471c19ee01b135ccf3", @@ -24,6 +25,7 @@ provider "registry.terraform.io/hashicorp/azurerm" { provider "registry.terraform.io/hashicorp/random" { version = "3.5.1" hashes = [ + "h1:3hjTP5tQBspPcFAJlfafnWrNrKnr7J4Cp0qB9jbqf30=", "h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=", "zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64", "zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d", diff --git a/CHANGELOG.md b/CHANGELOG.md index ee02ac833..07920a4dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ## Versjon 2024.07.30 - ✨ **Feide**. Man kan nå registrere bruker automatisk med Feide. +- ✨ **Fillagring**. Man kan nå slette en fil fra Azure basert på container navn og fil navn. ## Versjon 2024.05.01 - ⚡**Arrangement**. Et arrangement vil nå få kategori sendt som navn på kategori istedenfor kun id. diff --git a/app/common/file_handler.py b/app/common/file_handler.py index 590123d78..3b7450753 100644 --- a/app/common/file_handler.py +++ b/app/common/file_handler.py @@ -21,7 +21,7 @@ def getContainerNameFromBlob(self): return ( "".join(e for e in self.blob.content_type if e.isalnum()) if self.blob.content_type - else None + else "default" ) def checkBlobSize(self): diff --git a/app/content/serializers/user.py b/app/content/serializers/user.py index 86842860e..44a207122 100644 --- a/app/content/serializers/user.py +++ b/app/content/serializers/user.py @@ -5,22 +5,22 @@ from dry_rest_permissions.generics import DRYGlobalPermissionsField -from app.communication.notifier import Notify -from app.communication.enums import UserNotificationSettingType -from app.common.enums import GroupType, Groups +from app.common.enums import Groups, GroupType from app.common.serializers import BaseModelSerializer +from app.communication.enums import UserNotificationSettingType +from app.communication.notifier import Notify +from app.content.exceptions import FeideUserExistsError from app.content.models import User from app.content.serializers.user_bio import UserBioSerializer -from app.group.models import Group, Membership from app.content.util.feide_utils import ( + generate_random_password, get_feide_tokens, get_feide_user_groups, - parse_feide_groups, - generate_random_password, - get_study_year, get_feide_user_info_from_jwt, + get_study_year, + parse_feide_groups, ) -from app.content.exceptions import FeideUserExistsError +from app.group.models import Group, Membership class DefaultUserSerializer(BaseModelSerializer): @@ -168,9 +168,7 @@ def add_user_to_study(self, user, slug): study = Group.objects.filter(type=GroupType.STUDY, slug=slug).first() study_year = get_study_year(slug) class_ = Group.objects.get_or_create( - name=study_year, - type=GroupType.STUDYYEAR, - slug=study_year + name=study_year, type=GroupType.STUDYYEAR, slug=study_year ) if not study or not class_: diff --git a/app/content/urls.py b/app/content/urls.py index b7d6d0bcc..c5a482395 100644 --- a/app/content/urls.py +++ b/app/content/urls.py @@ -18,8 +18,9 @@ UserCalendarEvents, UserViewSet, accept_form, - upload, + delete, register_with_feide, + upload, ) router = routers.DefaultRouter() @@ -52,6 +53,7 @@ re_path(r"", include(router.urls)), path("accept-form/", accept_form), path("upload/", upload), + path("delete-file///", delete), path("feide/", register_with_feide), re_path(r"users/(?P[^/.]+)/events.ics", UserCalendarEvents()), ] diff --git a/app/content/util/feide_utils.py b/app/content/util/feide_utils.py index 8c0b82e6c..bc38cc963 100644 --- a/app/content/util/feide_utils.py +++ b/app/content/util/feide_utils.py @@ -1,11 +1,21 @@ -import jwt -import requests import secrets import string +from datetime import datetime +import jwt +import requests from requests.auth import HTTPBasicAuth -from datetime import datetime +from app.content.exceptions import ( + FeideGetTokenError, + FeideGetUserGroupsError, + FeideParseGroupsError, + FeideTokenNotFoundError, + FeideUsedUserCode, + FeideUserGroupsNotFoundError, + FeideUserInfoNotFoundError, + FeideUsernameNotFoundError, +) from app.settings import ( FEIDE_CLIENT_ID, FEIDE_CLIENT_SECRET, @@ -14,17 +24,6 @@ FEIDE_USER_GROUPS_INFO_URL, ) -from app.content.exceptions import ( - FeideTokenNotFoundError, - FeideGetTokenError, - FeideUserInfoNotFoundError, - FeideUsernameNotFoundError, - FeideUserGroupsNotFoundError, - FeideParseGroupsError, - FeideGetUserGroupsError, - FeideUsedUserCode, -) - def get_feide_tokens(code: str) -> tuple[str, str]: """Get access and JWT tokens for signed in Feide user""" diff --git a/app/content/views/__init__.py b/app/content/views/__init__.py index 50ff70bad..3392d438c 100644 --- a/app/content/views/__init__.py +++ b/app/content/views/__init__.py @@ -8,7 +8,7 @@ from app.content.views.news import NewsViewSet from app.content.views.page import PageViewSet from app.content.views.short_link import ShortLinkViewSet -from app.content.views.upload import upload +from app.content.views.upload import upload, delete from app.content.views.strike import StrikeViewSet from app.content.views.toddel import ToddelViewSet from app.content.views.qr_code import QRCodeViewSet diff --git a/app/content/views/feide.py b/app/content/views/feide.py index 57febec12..f09917b0f 100644 --- a/app/content/views/feide.py +++ b/app/content/views/feide.py @@ -1,9 +1,12 @@ -from rest_framework.decorators import api_view from rest_framework import status +from rest_framework.decorators import api_view from rest_framework.response import Response -from app.content.serializers import FeideUserCreateSerializer, DefaultUserSerializer from app.content.exceptions import FeideError +from app.content.serializers import ( + DefaultUserSerializer, + FeideUserCreateSerializer, +) @api_view(["POST"]) diff --git a/app/content/views/upload.py b/app/content/views/upload.py index b4e54a3b8..e203c0f76 100644 --- a/app/content/views/upload.py +++ b/app/content/views/upload.py @@ -38,3 +38,25 @@ def upload(request): {"detail": str(value_error)}, status=status.HTTP_400_BAD_REQUEST, ) + + +@api_view(["DELETE"]) +@permission_classes([IsMember]) +def delete(request, container_name, blob_name): + """Method for deleting files from Azure Blob Storage, only allowed for members""" + try: + handler = AzureFileHandler() + handler.blobName = blob_name + handler.containerName = container_name + + handler.deleteBlob() + return Response( + {"detail": "Filen ble slettet"}, + status=status.HTTP_200_OK, + ) + + except ValueError as value_error: + return Response( + {"detail": str(value_error)}, + status=status.HTTP_400_BAD_REQUEST, + ) diff --git a/infrastructure/containers.tf b/infrastructure/containers.tf index 856f795c7..65fb201f1 100644 --- a/infrastructure/containers.tf +++ b/infrastructure/containers.tf @@ -164,6 +164,26 @@ resource "azurerm_container_app" "lepton-api" { name = "VIPPS_ORDER_URL" value = var.vipps_order_url } + env { + name = "FEIDE_CLIENT_ID" + value = var.feide_client_id + } + env { + name = "FEIDE_CLIENT_SECRET" + value = var.feide_client_secret + } + env { + name = "FEIDE_TOKEN_URL" + value = var.feide_token_url + } + env { + name = "FEIDE_USER_GROUPS_INFO_URL" + value = var.feide_user_groups_info_url + } + env { + name = "FEIDE_REDIRECT_URL" + value = var.feide_redirect_url + } env { name = var.enviroment == "pro" ? "PROD" : "DEV" value = "true" @@ -337,6 +357,26 @@ resource "azurerm_container_app" "celery" { name = "VIPPS_ORDER_URL" value = var.vipps_order_url } + env { + name = "FEIDE_CLIENT_ID" + value = var.feide_client_id + } + env { + name = "FEIDE_CLIENT_SECRET" + value = var.feide_client_secret + } + env { + name = "FEIDE_TOKEN_URL" + value = var.feide_token_url + } + env { + name = "FEIDE_USER_GROUPS_INFO_URL" + value = var.feide_user_groups_info_url + } + env { + name = "FEIDE_REDIRECT_URL" + value = var.feide_redirect_url + } } } diff --git a/infrastructure/inputs.tf b/infrastructure/inputs.tf index c95871b34..55b271bea 100644 --- a/infrastructure/inputs.tf +++ b/infrastructure/inputs.tf @@ -64,6 +64,30 @@ variable "lepton_api_max_replicas" { default = 1 } +variable "feide_client_id" { + type = string + sensitive = true +} + +variable "feide_client_secret" { + type = string + sensitive = true +} + +variable "feide_token_url" { + type = string + default = "https://auth.dataporten.no/oauth/token" +} + +variable "feide_user_groups_info_url" { + type = string + default = "https://groups-api.dataporten.no/groups/me/groups" +} + +variable "feide_redirect_url" { + type = string +} + variable "enviroment" { type = string description = "value is either dev or pro" diff --git a/main.tf b/main.tf index 10b49d13f..50ad565bd 100644 --- a/main.tf +++ b/main.tf @@ -39,4 +39,10 @@ module "infrastructure" { lepton_api_min_replicas = var.lepton_api_min_replicas lepton_api_max_replicas = var.lepton_api_max_replicas + + feide_client_id = var.feide_client_id + feide_client_secret = var.feide_client_secret + feide_token_url = var.feide_token_url + feide_user_groups_info_url = var.feide_user_groups_info_url + feide_redirect_url = var.feide_redirect_url }