From b0afe73f85dcebf4a9e0c143b51c1802291c1959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Pav=C3=A3o?= Date: Fri, 12 Apr 2024 12:05:49 +0200 Subject: [PATCH] Revert "Merge develop into master (#1405)" This reverts commit a26fc360afca966992ffc171f0278b2b17753770. --- compute_worker/compute_worker.py | 15 +- docker-compose.yml | 2 +- src/apps/api/serializers/competitions.py | 6 - src/apps/api/serializers/submissions.py | 8 +- src/apps/api/views/submissions.py | 6 +- ...an_participants_make_submissions_public.py | 18 -- .../migrations/0048_auto_20240401_1646.py | 23 -- src/apps/competitions/models.py | 7 - src/apps/competitions/unpackers/v1.py | 2 - src/apps/competitions/unpackers/v2.py | 3 - src/apps/oidc_configurations/__init__.py | 0 src/apps/oidc_configurations/admin.py | 6 - src/apps/oidc_configurations/apps.py | 5 - .../migrations/0001_initial.py | 29 --- .../migrations/__init__.py | 0 src/apps/oidc_configurations/models.py | 14 -- src/apps/oidc_configurations/urls.py | 10 - src/apps/oidc_configurations/views.py | 203 ------------------ .../migrations/0013_auto_20240304_0616.py | 25 --- src/apps/profiles/models.py | 5 - src/apps/profiles/views.py | 6 - src/settings/base.py | 1 - .../competitions/detail/_detailed_results.tag | 1 + .../riot/competitions/detail/leaderboards.tag | 8 +- .../detail/submission_manager.tag | 13 +- .../competitions/detail/submission_modal.tag | 2 +- .../editor/_competition_details.tag | 135 +++--------- src/templates/oidc/oidc_complete.html | 16 -- src/templates/registration/login.html | 26 --- src/tests/functional/test_submissions.py | 11 +- src/urls.py | 2 - 31 files changed, 50 insertions(+), 558 deletions(-) delete mode 100644 src/apps/competitions/migrations/0047_competition_can_participants_make_submissions_public.py delete mode 100644 src/apps/competitions/migrations/0048_auto_20240401_1646.py delete mode 100644 src/apps/oidc_configurations/__init__.py delete mode 100644 src/apps/oidc_configurations/admin.py delete mode 100644 src/apps/oidc_configurations/apps.py delete mode 100644 src/apps/oidc_configurations/migrations/0001_initial.py delete mode 100644 src/apps/oidc_configurations/migrations/__init__.py delete mode 100644 src/apps/oidc_configurations/models.py delete mode 100644 src/apps/oidc_configurations/urls.py delete mode 100644 src/apps/oidc_configurations/views.py delete mode 100644 src/apps/profiles/migrations/0013_auto_20240304_0616.py delete mode 100644 src/templates/oidc/oidc_complete.html diff --git a/compute_worker/compute_worker.py b/compute_worker/compute_worker.py index 24e7d25bc..491b84e5f 100644 --- a/compute_worker/compute_worker.py +++ b/compute_worker/compute_worker.py @@ -507,25 +507,12 @@ async def _run_container_engine_cmd(self, engine_cmd, kind): websocket = await websockets.connect(self.websocket_url) websocket_errors = (socket.gaierror, websockets.WebSocketException, websockets.ConnectionClosedError, ConnectionRefusedError) - # Function to read a line, if the line is larger than the buffer size we will - # return the buffer so we can continue reading until we get a newline, rather - # than getting a LimitOverrunError - async def _readline_or_chunk(stream): - try: - return await stream.readuntil(b"\n") - except asyncio.exceptions.IncompleteReadError as e: - # Just return what has been read so far - return e.partial - except asyncio.exceptions.LimitOverrunError as e: - # If we get a LimitOverrunError, we will return the buffer so we can continue reading - return await stream.read(e.consumed) - while any(v["continue"] for k, v in self.logs[kind].items() if k in ['stdout', 'stderr']): try: logs = [self.logs[kind][key] for key in ('stdout', 'stderr')] for value in logs: try: - out = await asyncio.wait_for(_readline_or_chunk(value["stream"]), timeout=.1) + out = await asyncio.wait_for(value["stream"].readline(), timeout=.1) if out: value["data"] += out print("WS: " + str(out)) diff --git a/docker-compose.yml b/docker-compose.yml index 08e7a5c84..0b5c2c6ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -231,4 +231,4 @@ services: logging: options: max-size: "20k" - max-file: "10" \ No newline at end of file + max-file: "10" diff --git a/src/apps/api/serializers/competitions.py b/src/apps/api/serializers/competitions.py index 41cd51341..9b622b3dc 100644 --- a/src/apps/api/serializers/competitions.py +++ b/src/apps/api/serializers/competitions.py @@ -254,10 +254,7 @@ class Meta: 'registration_auto_approve', 'queue', 'enable_detailed_results', - 'show_detailed_results_in_submission_panel', - 'show_detailed_results_in_leaderboard', 'auto_run_submissions', - 'can_participants_make_submissions_public', 'make_programs_available', 'make_input_data_available', 'docker_image', @@ -376,10 +373,7 @@ class Meta: 'submission_count', 'queue', 'enable_detailed_results', - 'show_detailed_results_in_submission_panel', - 'show_detailed_results_in_leaderboard', 'auto_run_submissions', - 'can_participants_make_submissions_public', 'make_programs_available', 'make_input_data_available', 'docker_image', diff --git a/src/apps/api/serializers/submissions.py b/src/apps/api/serializers/submissions.py index 05191e8e3..33fe9241e 100644 --- a/src/apps/api/serializers/submissions.py +++ b/src/apps/api/serializers/submissions.py @@ -42,7 +42,6 @@ class SubmissionSerializer(serializers.ModelSerializer): task = TaskSerializer() created_when = serializers.DateTimeField(format="%Y-%m-%d %H:%M") auto_run = serializers.SerializerMethodField(read_only=True) - can_make_submissions_public = serializers.SerializerMethodField(read_only=True) class Meta: model = Submission @@ -68,8 +67,7 @@ class Meta: 'leaderboard', 'on_leaderboard', 'task', - 'auto_run', - 'can_make_submissions_public', + 'auto_run' ) read_only_fields = ( 'pk', @@ -87,10 +85,6 @@ def get_auto_run(self, instance): # returns this submission's competition auto_run_submissions Flag return instance.phase.competition.auto_run_submissions - def get_can_make_submissions_public(self, instance): - # returns this submission's competition can_participants_make_submissions_public Flag - return instance.phase.competition.can_participants_make_submissions_public - class SubmissionLeaderBoardSerializer(serializers.ModelSerializer): scores = SubmissionScoreSerializer(many=True) diff --git a/src/apps/api/views/submissions.py b/src/apps/api/views/submissions.py index fccaeefd4..d0c0defb7 100644 --- a/src/apps/api/views/submissions.py +++ b/src/apps/api/views/submissions.py @@ -352,15 +352,15 @@ def get_detail_result(self, request, pk): ) else: return Response({ - "error_msg": "Detailed results are disable for this competition!"}, + "error_msg": "Visualizations are disabled"}, status=status.HTTP_404_NOT_FOUND ) @action(detail=True, methods=('GET',)) def toggle_public(self, request, pk): submission = super().get_object() - if not submission.phase.competition.can_participants_make_submissions_public: - raise PermissionDenied("You do not have permission to make this submissions public/private") + if not self.has_admin_permission(request.user, submission): + raise PermissionDenied(f'You do not have permission to publish this submissions') is_public = not submission.is_public submission.data.is_public = is_public submission.data.save(send=False) diff --git a/src/apps/competitions/migrations/0047_competition_can_participants_make_submissions_public.py b/src/apps/competitions/migrations/0047_competition_can_participants_make_submissions_public.py deleted file mode 100644 index 2b750fa02..000000000 --- a/src/apps/competitions/migrations/0047_competition_can_participants_make_submissions_public.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.17 on 2024-03-28 13:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('competitions', '0046_merge_20240222_1916'), - ] - - operations = [ - migrations.AddField( - model_name='competition', - name='can_participants_make_submissions_public', - field=models.BooleanField(default=True), - ), - ] diff --git a/src/apps/competitions/migrations/0048_auto_20240401_1646.py b/src/apps/competitions/migrations/0048_auto_20240401_1646.py deleted file mode 100644 index 3ed2ad446..000000000 --- a/src/apps/competitions/migrations/0048_auto_20240401_1646.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2.17 on 2024-04-01 16:46 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('competitions', '0047_competition_can_participants_make_submissions_public'), - ] - - operations = [ - migrations.AddField( - model_name='competition', - name='show_detailed_results_in_leaderboard', - field=models.BooleanField(default=True), - ), - migrations.AddField( - model_name='competition', - name='show_detailed_results_in_submission_panel', - field=models.BooleanField(default=True), - ), - ] diff --git a/src/apps/competitions/models.py b/src/apps/competitions/models.py index 777dbb86a..03d8b65fc 100644 --- a/src/apps/competitions/models.py +++ b/src/apps/competitions/models.py @@ -49,10 +49,6 @@ class Competition(ChaHubSaveMixin, models.Model): description = models.TextField(null=True, blank=True) docker_image = models.CharField(max_length=128, default="codalab/codalab-legacy:py37") enable_detailed_results = models.BooleanField(default=False) - # If true, show detailed results in submission panel - show_detailed_results_in_submission_panel = models.BooleanField(default=True) - # If true, show detailed results in leaderboard - show_detailed_results_in_leaderboard = models.BooleanField(default=True) make_programs_available = models.BooleanField(default=False) make_input_data_available = models.BooleanField(default=False) @@ -73,9 +69,6 @@ class Competition(ChaHubSaveMixin, models.Model): # if false, submissions run will be intiiated by organizer auto_run_submissions = models.BooleanField(default=True) - # If true, participants see the make their submissions public - can_participants_make_submissions_public = models.BooleanField(default=True) - def __str__(self): return f"competition-{self.title}-{self.pk}-{self.competition_type}" diff --git a/src/apps/competitions/unpackers/v1.py b/src/apps/competitions/unpackers/v1.py index dc476a5c2..91496ddca 100644 --- a/src/apps/competitions/unpackers/v1.py +++ b/src/apps/competitions/unpackers/v1.py @@ -23,8 +23,6 @@ def __init__(self, *args, **kwargs): "description": self.competition_yaml.get("description", ""), "docker_image": docker_image, "enable_detailed_results": self.competition_yaml.get('enable_detailed_results', False), - "show_detailed_results_in_submission_panel": self.competition_yaml.get('show_detailed_results_in_submission_panel', True), - "show_detailed_results_in_leaderboard": self.competition_yaml.get('show_detailed_results_in_leaderboard', True), "auto_run_submissions": self.competition_yaml.get('auto_run_submissions', True), "make_programs_available": self.competition_yaml.get('make_programs_available', False), "make_input_data_available": self.competition_yaml.get('make_input_data_available', False), diff --git a/src/apps/competitions/unpackers/v2.py b/src/apps/competitions/unpackers/v2.py index b825e9ee2..8de2a42ad 100644 --- a/src/apps/competitions/unpackers/v2.py +++ b/src/apps/competitions/unpackers/v2.py @@ -14,10 +14,7 @@ def __init__(self, *args, **kwargs): "registration_auto_approve": self.competition_yaml.get('registration_auto_approve', False), "docker_image": self.competition_yaml.get('docker_image', 'codalab/codalab-legacy:py37'), "enable_detailed_results": self.competition_yaml.get('enable_detailed_results', False), - "show_detailed_results_in_submission_panel": self.competition_yaml.get('show_detailed_results_in_submission_panel', True), - "show_detailed_results_in_leaderboard": self.competition_yaml.get('show_detailed_results_in_leaderboard', True), "auto_run_submissions": self.competition_yaml.get('auto_run_submissions', True), - "can_participants_make_submissions_public": self.competition_yaml.get('can_participants_make_submissions_public', True), "make_programs_available": self.competition_yaml.get('make_programs_available', False), "make_input_data_available": self.competition_yaml.get('make_input_data_available', False), "description": self.competition_yaml.get("description", ""), diff --git a/src/apps/oidc_configurations/__init__.py b/src/apps/oidc_configurations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/apps/oidc_configurations/admin.py b/src/apps/oidc_configurations/admin.py deleted file mode 100644 index 5ea6e683f..000000000 --- a/src/apps/oidc_configurations/admin.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.contrib import admin -from .models import Auth_Organization - -admin.site.register(Auth_Organization) - -# Register your models here. diff --git a/src/apps/oidc_configurations/apps.py b/src/apps/oidc_configurations/apps.py deleted file mode 100644 index 3d757062b..000000000 --- a/src/apps/oidc_configurations/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class OidcConfigurationsConfig(AppConfig): - name = 'oidc_configurations' diff --git a/src/apps/oidc_configurations/migrations/0001_initial.py b/src/apps/oidc_configurations/migrations/0001_initial.py deleted file mode 100644 index 085e64983..000000000 --- a/src/apps/oidc_configurations/migrations/0001_initial.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 2.2.17 on 2024-03-04 06:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='Auth_Organization', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('client_id', models.CharField(max_length=255)), - ('client_secret', models.CharField(max_length=255)), - ('authorization_url', models.CharField(max_length=255)), - ('token_url', models.CharField(max_length=255)), - ('user_info_url', models.CharField(max_length=255)), - ('redirect_url', models.CharField(max_length=255)), - ('button_bg_color', models.CharField(default='#2C3E4C', max_length=20)), - ('button_text_color', models.CharField(default='#FFFFFF', max_length=20)), - ], - ), - ] diff --git a/src/apps/oidc_configurations/migrations/__init__.py b/src/apps/oidc_configurations/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/apps/oidc_configurations/models.py b/src/apps/oidc_configurations/models.py deleted file mode 100644 index 9e2b0c66c..000000000 --- a/src/apps/oidc_configurations/models.py +++ /dev/null @@ -1,14 +0,0 @@ -# oidc_configurations/models.py -from django.db import models - - -class Auth_Organization(models.Model): - name = models.CharField(max_length=255) - client_id = models.CharField(max_length=255) - client_secret = models.CharField(max_length=255) - authorization_url = models.CharField(max_length=255) - token_url = models.CharField(max_length=255) - user_info_url = models.CharField(max_length=255) - redirect_url = models.CharField(max_length=255) - button_bg_color = models.CharField(max_length=20, default='#2C3E4C') - button_text_color = models.CharField(max_length=20, default='#FFFFFF') diff --git a/src/apps/oidc_configurations/urls.py b/src/apps/oidc_configurations/urls.py deleted file mode 100644 index 7bfae4f99..000000000 --- a/src/apps/oidc_configurations/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -# oidc_configurations/urls.py -from django.urls import path -from .views import organization_oidc_login, oidc_complete - -app_name = 'oidc_configurations' - -urlpatterns = [ - path('organization_oidc_login/', organization_oidc_login, name='organization_oidc_login'), - path('complete//', oidc_complete, name='oidc_complete'), -] diff --git a/src/apps/oidc_configurations/views.py b/src/apps/oidc_configurations/views.py deleted file mode 100644 index 6b04be8ff..000000000 --- a/src/apps/oidc_configurations/views.py +++ /dev/null @@ -1,203 +0,0 @@ -# oidc_configurations/views.py -import base64 -import requests -from django.shortcuts import render, redirect, get_object_or_404 -from .models import Auth_Organization -from django.contrib.auth import get_user_model, login -import re - -User = get_user_model() - -BACKEND = 'django.contrib.auth.backends.ModelBackend' - - -def organization_oidc_login(request): - # Check if this is a post request and it contains organization_oauth2_login - if request.method == 'POST' and 'organization_oidc_login' in request.POST: - # Get auth organization id from the request - auth_organization_id = request.POST.get('organization_oidc_login') - - # Get auth organization using its id - organization = get_object_or_404(Auth_Organization, pk=auth_organization_id) - - if organization: - # Create a redirect url consisiting of - # - authorization_url - # - client_id - # - response_type - # - redirect_uri - oidc_auth_url = ( - f"{organization.authorization_url}?" - f"client_id={organization.client_id}&" - "response_type=code&" - "scope=openid profile email&" - f"redirect_uri={organization.redirect_url}" - ) - - # Redirect the user to the OIDC provider's authorization URL - return redirect(oidc_auth_url) - - # Handle other cases or render a different template if needed - return render(request, 'registration/login.html') - - -def oidc_complete(request, auth_organization_id): - - # create empty context - context = {} - - # Get error or authorization code from the query string - error = request.GET.get('error', None) - error_description = request.GET.get('error_description', None) - authorization_code = request.GET.get('code', None) - - if error: - context["error"] = error - - if error_description: - context["error_description"] = error_description - - # Token exhange process - if authorization_code: - - try: - # STEP 1: Get auth organization using its id - organization = get_object_or_404(Auth_Organization, pk=auth_organization_id) - - if organization: - - # STEP 2: Get access token - access_token, token_error = get_access_token(organization, authorization_code) - - if token_error: - context["error"] = token_error - else: - # STEP 3: Get user info - user_info, user_info_error = get_user_info(organization, access_token) - if user_info_error: - context["error"] = user_info_error - else: - - # get email and nickname (username) of the user - user_email = user_info.get("email", None) - user_nickname = user_info.get("nickname", None) - if user_email: - # get user with this email - user = get_user_by_email(user_email) - # STEP 4: Check if user exists and user is created using oidc and oidc orgnaization matches this one - if user: - login(request, user, backend=BACKEND) - # Redirect the user home page - return redirect('pages:home') - else: - return register_and_authenticate_user(request, user_email, user_nickname, organization) - - else: - context["error"] = "Unable to extract email from user info! Please contact platform" - else: - context["error"] = "Invalid Organization ID!" - except Exception as e: - context["error"] = f"{e}" - - return render(request, 'oidc/oidc_complete.html', context) - - -def get_access_token(organization, authorization_code): - - token_url = organization.token_url - client_id = organization.client_id - client_secret = organization.client_secret - redirect_url = organization.redirect_url - - auth_header = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode("utf-8") - headers = { - "Content-Type": "application/x-www-form-urlencoded", - "Authorization": f"Basic {auth_header}", - } - data = { - "grant_type": "authorization_code", - "code": authorization_code, - "redirect_uri": redirect_url, - } - - try: - response = requests.request("POST", token_url, data=data, headers=headers) - response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx) - token_data = response.json() - access_token = token_data.get('access_token') - return access_token, None - except requests.exceptions.RequestException as e: - print(f"Error during token request: {e}") - return None, e - except Exception as e: - print(f"Error parsing token response: {e}") - return None, e - - -def get_user_info(organization, access_token): - - user_info_url = organization.user_info_url - - headers = { - 'Authorization': f'Bearer {access_token}', - } - - response = requests.get(user_info_url, headers=headers) - - try: - user_info = response.json() - return user_info, None - except Exception as e: - return None, e - - -def register_and_authenticate_user(request, user_email, user_nickname, organization): - - if not user_nickname: - username = re.sub(r'[^a-zA-Z0-9]', '', user_email.split('@')[0]) - else: - username = user_nickname - - # Ensure the username is unique - username = create_unique_username(username) - - # Create a new user - user = User.objects.create( - username=username, - email=user_email, - is_created_using_oidc=True, - oidc_organization=organization, - ) - - if user: - # login user - login(request, user, backend=BACKEND) - # Redirect to the home page - return redirect('pages:home') - - else: - # Handle authentication failure i.e. go back to login - return redirect('accounts:login') - - -def create_unique_username(username): - # Check if the username already exists - if User.objects.filter(username=username).exists(): - # If the username already exists, modify it to make it unique - suffix = 1 - new_username = f"{username}_{suffix}" - while User.objects.filter(username=new_username).exists(): - suffix += 1 - new_username = f"{username}_{suffix}" - return new_username - else: - # If the username doesn't exist, use it as is - return username - - -def get_user_by_email(email): - try: - user = User.objects.get(email=email) - return user - except User.DoesNotExist: - return None diff --git a/src/apps/profiles/migrations/0013_auto_20240304_0616.py b/src/apps/profiles/migrations/0013_auto_20240304_0616.py deleted file mode 100644 index 121ca477c..000000000 --- a/src/apps/profiles/migrations/0013_auto_20240304_0616.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 2.2.17 on 2024-03-04 06:16 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('oidc_configurations', '0001_initial'), - ('profiles', '0012_user_quota'), - ] - - operations = [ - migrations.AddField( - model_name='user', - name='is_created_using_oidc', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='user', - name='oidc_organization', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='authorized_users', to='oidc_configurations.Auth_Organization'), - ), - ] diff --git a/src/apps/profiles/models.py b/src/apps/profiles/models.py index b150e54e4..518230f92 100644 --- a/src/apps/profiles/models.py +++ b/src/apps/profiles/models.py @@ -16,7 +16,6 @@ When, DecimalField, ) -from oidc_configurations.models import Auth_Organization PROFILE_DATA_BLACKLIST = [ 'password', @@ -73,10 +72,6 @@ class User(ChaHubSaveMixin, AbstractBaseUser, PermissionsMixin): is_staff = models.BooleanField(default=False) quota = models.BigIntegerField(default=settings.DEFAULT_USER_QUOTA, null=False) - # Fields for OIDC authentication - is_created_using_oidc = models.BooleanField(default=False) - oidc_organization = models.ForeignKey(Auth_Organization, null=True, blank=True, on_delete=models.SET_NULL, related_name="authorized_users") - # Notifications organizer_direct_message_updates = models.BooleanField(default=True) allow_forum_notifications = models.BooleanField(default=True) diff --git a/src/apps/profiles/views.py b/src/apps/profiles/views.py index a6a21cb64..3b6b22169 100644 --- a/src/apps/profiles/views.py +++ b/src/apps/profiles/views.py @@ -21,7 +21,6 @@ UserNotificationSerializer from .forms import SignUpForm, LoginForm from .models import User, Organization, Membership -from oidc_configurations.models import Auth_Organization from .tokens import account_activation_token @@ -179,11 +178,6 @@ def log_in(request): else: context['form'] = form - # Fetch auth_organizations from the database - auth_organizations = Auth_Organization.objects.all() - if auth_organizations: - context['auth_organizations'] = auth_organizations - if not context.get('form'): context['form'] = LoginForm() return render(request, 'registration/login.html', context) diff --git a/src/settings/base.py b/src/settings/base.py index 27e76045a..b8d133a05 100644 --- a/src/settings/base.py +++ b/src/settings/base.py @@ -60,7 +60,6 @@ 'health', 'forums', 'announcements', - 'oidc_configurations', ) INSTALLED_APPS = THIRD_PARTY_APPS + OUR_APPS diff --git a/src/static/riot/competitions/detail/_detailed_results.tag b/src/static/riot/competitions/detail/_detailed_results.tag index bb2f87e30..0136e6689 100644 --- a/src/static/riot/competitions/detail/_detailed_results.tag +++ b/src/static/riot/competitions/detail/_detailed_results.tag @@ -1,4 +1,5 @@ +

Detailed Results