diff --git a/Dockerfile.compute_worker b/Dockerfile.compute_worker index 8934fb37b..77e0ef69d 100644 --- a/Dockerfile.compute_worker +++ b/Dockerfile.compute_worker @@ -1,4 +1,4 @@ -FROM python:3.8 +FROM --platform=linux/amd64 python:3.8 # This makes output not buffer and return immediately, nice for seeing results in stdout ENV PYTHONUNBUFFERED 1 diff --git a/Dockerfile.compute_worker_gpu b/Dockerfile.compute_worker_gpu index a8d9038d8..f2110647c 100644 --- a/Dockerfile.compute_worker_gpu +++ b/Dockerfile.compute_worker_gpu @@ -1,4 +1,4 @@ -FROM python:3.8.1-buster +FROM --platform=linux/amd64 python:3.8.1-buster # We need curl to get docker/nvidia-docker RUN apt-get update && apt-get install curl wget -y @@ -6,8 +6,8 @@ RUN apt-get update && apt-get install curl wget -y # This makes output not buffer and return immediately, nice for seeing results in stdout ENV PYTHONUNBUFFERED 1 -# Install a specific version of docker -RUN curl -sSL https://get.docker.com/ | sed 's/docker-ce/docker-ce=18.03.0~ce-0~debian/' | sh +# Install Docker +RUN apt-get update && curl -fsSL https://get.docker.com | sh # nvidia-docker jazz RUN curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | apt-key add - diff --git a/docker/compute_worker/compute_worker.py b/docker/compute_worker/compute_worker.py index 8f13a5a85..110010ad3 100644 --- a/docker/compute_worker/compute_worker.py +++ b/docker/compute_worker/compute_worker.py @@ -484,7 +484,7 @@ async def _run_program_directory(self, program_dir, kind, can_be_output=False): with open(os.path.join(program_dir, metadata_path), 'r') as metadata_file: metadata = yaml.load(metadata_file.read(), Loader=yaml.FullLoader) logger.info(f"Metadata contains:\n {metadata}") - command = metadata.get("command") + command = metadata.get("command") if metadata is not None else None # in case the file exists but is empty if not command and kind == "ingestion": raise SubmissionException("Program directory missing 'command' in metadata") elif not command: diff --git a/requirements.txt b/requirements.txt index b8c5a25b0..5a14473b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ django-oauth-toolkit==1.0.0 django-cors-middleware==1.5.0 social-auth-core==2.0.0 social-auth-app-django==3.1.0 +six==1.16.0 django-extensions==2.2.6 channels==2.4 channels_redis==3.2.0 diff --git a/src/apps/profiles/tokens.py b/src/apps/profiles/tokens.py new file mode 100644 index 000000000..13439a6b0 --- /dev/null +++ b/src/apps/profiles/tokens.py @@ -0,0 +1,10 @@ +from django.contrib.auth.tokens import PasswordResetTokenGenerator +import six + + +class AccountActivationTokenGenerator(PasswordResetTokenGenerator): + def _make_hash_value(self, user, timestamp): + return (six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.is_active)) + + +account_activation_token = AccountActivationTokenGenerator() diff --git a/src/apps/profiles/urls.py b/src/apps/profiles/urls.py index 085917008..f989ad405 100644 --- a/src/apps/profiles/urls.py +++ b/src/apps/profiles/urls.py @@ -6,6 +6,7 @@ urlpatterns = [ # url(r'^signup', views.sign_up, name="signup"), + path('activate//', views.activate, name='activate'), path('user//edit/', views.UserEditView.as_view(), name='user_edit'), path('user//notifications/', views.UserNotificationEdit.as_view(), name="user_notifications"), path('user//', views.UserDetailView.as_view(), name="user_profile"), diff --git a/src/apps/profiles/urls_accounts.py b/src/apps/profiles/urls_accounts.py index 7b956f9f7..6266d5216 100644 --- a/src/apps/profiles/urls_accounts.py +++ b/src/apps/profiles/urls_accounts.py @@ -5,7 +5,6 @@ app_name = "accounts" - urlpatterns = [ url(r'^signup', views.sign_up, name="signup"), # url(r'^user_profile', views.user_profile, name="user_profile"), diff --git a/src/apps/profiles/views.py b/src/apps/profiles/views.py index 581112b4f..93687968f 100644 --- a/src/apps/profiles/views.py +++ b/src/apps/profiles/views.py @@ -1,17 +1,24 @@ import json from django.conf import settings -from django.contrib.auth import authenticate, login +from django.contrib import messages +from django.contrib.auth import authenticate +from django.contrib.sites.shortcuts import get_current_site +from django.core.mail import EmailMessage from django.http import Http404 from django.shortcuts import render, redirect from django.contrib.auth import views as auth_views from django.contrib.auth.mixins import LoginRequiredMixin +from django.template.loader import render_to_string +from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode +from django.utils.encoding import force_bytes, force_str from django.views.generic import DetailView, TemplateView from api.serializers.profiles import UserSerializer, OrganizationDetailSerializer, OrganizationEditSerializer, \ UserNotificationSerializer from .forms import SignUpForm from .models import User, Organization, Membership +from .tokens import account_activation_token class LoginView(auth_views.LoginView): @@ -57,6 +64,44 @@ def get_context_data(self, **kwargs): return context +def activate(request, uidb64, token): + try: + # import pdb; pdb.set_trace(); + uid = force_str(urlsafe_base64_decode(uidb64)) + user = User.objects.get(pk=uid) + except User.DoesNotExist: + user = None + messages.error(request, f"User not found. Please sign up again.") + return redirect('accounts:signup') + if user is not None and account_activation_token.check_token(user, token): + user.is_active = True + user.save() + messages.success(request, f'Your account is fully setup! Please login.') + return redirect('accounts:login') + else: + user.delete() + messages.error(request, f"Activation link is invalid. Please sign up again.") + return redirect('accounts:signup') + return redirect('pages:home') + + +def activateEmail(request, user, to_email): + mail_subject = 'Activate your user account.' + message = render_to_string('profiles/emails/template_activate_account.html', { + 'user': user.username, + 'domain': get_current_site(request).domain, + 'uid': urlsafe_base64_encode(force_bytes(user.pk)), + 'token': account_activation_token.make_token(user), + 'protocol': 'https' if request.is_secure() else 'http' + }) + email = EmailMessage(mail_subject, message, to=[to_email]) + if email.send(): + messages.success(request, f'Dear {user.username}, please go to you email {to_email} inbox and click on \ + received activation link to confirm and complete the registration. *Note: Check your spam folder.') + else: + messages.error(request, f'Problem sending confirmation email to {to_email}, check if you typed it correctly.') + + def sign_up(request): context = {} context['chahub_signup_url'] = "{}/profiles/signup?next={}/social/login/chahub".format( @@ -70,7 +115,9 @@ def sign_up(request): username = form.cleaned_data.get('username') raw_password = form.cleaned_data.get('password1') user = authenticate(username=username, password=raw_password) - login(request, user, backend='django.contrib.auth.backends.ModelBackend') + user.is_active = False + user.save() + activateEmail(request, user, form.cleaned_data.get('email')) return redirect('pages:home') else: context['form'] = form diff --git a/src/templates/base.html b/src/templates/base.html index fc7221889..850123411 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -25,6 +25,37 @@ {% endblock %} + +{% if messages %} + {% for message in messages %} + {% if message.tags == 'success'%} + + {% elif message.tags == 'info' %} + + {% elif message.tags == 'warning' %} + + {% elif message.tags == 'error' %} + + {% endif %} + {% endfor %} +{% endif %} + +
diff --git a/src/templates/profiles/emails/template_activate_account.html b/src/templates/profiles/emails/template_activate_account.html new file mode 100644 index 000000000..98ee27122 --- /dev/null +++ b/src/templates/profiles/emails/template_activate_account.html @@ -0,0 +1,10 @@ +{% autoescape off %} +Hi {{ user.username }}, + +Please click on the link below to confirm your registration: + +{{ protocol }}://{{ domain }}{% url 'profiles:activate' uidb64=uid token=token %} + +This is an automatic message delivered by Codabench. + +{% endautoescape %}