Commit 39a6a185 authored by BERJON Matthieu's avatar BERJON Matthieu
Browse files

Merge branch 'django' of gitlab.inria.fr:allgo/allgo into 250-implement-access-control-in-job-views


Signed-off-by: BERJON Matthieu's avatarMatthieu Berjon <matthieu.berjon@inria.fr>
parents 002574d6 13aa574a
Pipeline #40822 failed with stage
in 2 minutes
......@@ -79,7 +79,7 @@ class UserWebappForm(forms.ModelForm):
class Meta:
model = Webapp
fields = ('name', 'description', 'contact', 'notebook_gitrepo',
'private', 'owner', 'docker_os', 'tags')
'private', 'owner', 'docker_os', 'tags', 'entrypoint')
class HomeSignupForm(SignupForm):
......@@ -180,6 +180,8 @@ class WebappForm(forms.ModelForm):
initial=1,
label='Default queue type',
label_suffix='')
entrypoint = forms.CharField(label="Entrypoint", label_suffix="",
initial="/home/allgo/entrypoint")
def __init__(self, *args, **kwargs):
super(WebappForm, self).__init__(*args, **kwargs)
......@@ -198,7 +200,7 @@ class WebappForm(forms.ModelForm):
class Meta:
model = Webapp
fields = ('name', 'description', 'logo_file_name', 'contact', 'entrypoint', 'job_queue', 'private', 'docker_os')
fields = ('name', 'description', 'logo_file_name', 'contact', 'entrypoint', 'job_queue', 'private', 'docker_os', 'entrypoint')
class WebappSandboxForm(forms.ModelForm):
......
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from .models import Job
class GroupRequiredMixin(object):
class IsProviderMixin(object):
"""Authorize a user to access specific views
"""
group_required - list of strings, required param
"""
group_required = None
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated():
raise PermissionDenied
local_part, domain = request.user.email.split("@")
if domain in settings.ALLOWED_DEVELOPER_DOMAINS:
return super().dispatch(request, *args, **kwargs)
else:
user_groups = []
for group in request.user.groups.values_list('name', flat=True):
user_groups.append(group)
if len(set(user_groups).intersection(self.group_required)) <= 0:
raise PermissionDenied
return super(GroupRequiredMixin, self).dispatch(request, *args, **kwargs)
raise PermissionDenied
class JobAuthorizationMixin(object):
......
......@@ -2,6 +2,7 @@ from __future__ import unicode_literals
import os
from django.conf import settings
from django.contrib import auth
from django.contrib.auth.models import User, AnonymousUser
from django.core.validators import MinLengthValidator, MinValueValidator, \
RegexValidator
......@@ -17,6 +18,7 @@ from .validators import job_param_validator, docker_container_id_validator, \
sshkey_validator, token_validator
def generate_token(length=32):
""" Generate a random string according to its length.
......@@ -610,8 +612,18 @@ def save_user_profile(sender, instance, **kwargs):
instance.allgouser.save()
def is_provider(user):
"""Return true if the user belongs to the allowed developer domains
"""
local_part, domain = user.email.split("@")
if domain in settings.ALLOWED_DEVELOPER_DOMAINS:
return True
else:
return False
# Add the `is_provider` function as a `User` model method
auth.models.User.add_to_class('is_provider', is_provider)
# NOTE: because there is a circular dependency between models.py and
# helpers.py, we have to do this import after 'Job' and 'Webapp' are defined
from .helpers import is_allowed_ip_admin
from django import template
from django.contrib.auth.models import Group
register = template.Library()
@register.filter(name='has_group')
def has_group(user, group_name):
group = Group.objects.get(name=group_name)
return group in user.groups.all()
......@@ -61,8 +61,8 @@ from .forms import (
)
# Local imports
import config
from .helpers import get_base_url, get_ssh_data, upload_data, notify_controller
from .mixins import GroupRequiredMixin, JobAuthorizationMixin
from .helpers import get_base_url, get_ssh_data, upload_data, notify_controller, lookup_job_file
from .mixins import IsProviderMixin, JobAuthorizationMixin
from .models import (
AllgoUser,
DockerOs,
......@@ -228,7 +228,7 @@ class WebappUpdate(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
return super(WebappUpdate, self).form_invalid(form)
class WebappCreate(SuccessMessageMixin, LoginRequiredMixin, GroupRequiredMixin, CreateView):
class WebappCreate(SuccessMessageMixin, LoginRequiredMixin, IsProviderMixin, CreateView):
"""Create a new webapp
Attributes:
......@@ -243,7 +243,7 @@ class WebappCreate(SuccessMessageMixin, LoginRequiredMixin, GroupRequiredMixin,
form_class = WebappForm
success_message = 'Webapp created successfully.'
template_name = 'webapp_add.html'
group_required = ['inria', ]
# group_required = ['inria', ]
def get_success_url(self):
"""If successful redirect to the webapp list page"""
......@@ -347,8 +347,8 @@ class WebappSandboxPanel(LoginRequiredMixin, TemplateView):
for state in (WebappVersion.READY, WebappVersion.COMMITTED):
versions.update((v.number, v) for v in WebappVersion.objects.filter(
webapp=context["webapp"], state=state))
context['versions'] = reversed(
natsort.versorted(versions.values(), key=lambda v: v.number))
context['versions'] = natsort.versorted(versions.values(), key=lambda v: v.number)
context['versions'].reverse()
return context
def post(self, request, *, docker_name):
......@@ -744,9 +744,11 @@ class JobCreate(SuccessMessageMixin, CreateView):
model = Job
form_class = JobForm
success_message = 'Job created successfully.'
success_url = reverse_lazy('main:job_list')
template_name = 'webapp_detail.html'
def get_success_url(self):
return reverse('main:job_detail', args=(self.job_id,))
def form_valid(self, form):
"""Save data coming from the form in the database """
webapp = Webapp.objects.get(docker_name=self.kwargs['docker_name'])
......@@ -773,6 +775,7 @@ class JobCreate(SuccessMessageMixin, CreateView):
# start the job
obj.state = Job.WAITING
obj.save()
self.job_id = obj.id
return super().form_valid(form)
......@@ -1006,7 +1009,7 @@ class RunnerList(LoginRequiredMixin, ListView):
return super().get_context_data(**kwargs)
class RunnerCreate(SuccessMessageMixin, LoginRequiredMixin, GroupRequiredMixin, CreateView):
class RunnerCreate(SuccessMessageMixin, LoginRequiredMixin, IsProviderMixin, CreateView):
"""Create a runner and save it into the database
Attributes:
......@@ -1022,7 +1025,6 @@ class RunnerCreate(SuccessMessageMixin, LoginRequiredMixin, GroupRequiredMixin,
error_message = 'You don\'t have sufficient privileges to create an open bar runner.'
success_url = reverse_lazy('main:runner_list')
template_name = 'runner_add_update.html'
group_required = ['inria', ]
def form_valid(self, form):
""" Validate some fields before saving them."""
......
{% extends "base.html" %}
{% load static groups converters humanize %}
{% load static converters humanize %}
{% block title %}Available apps for tag {{ tag | title }}{% endblock %}
......
......@@ -99,8 +99,15 @@
{% endfor %}
</select>
</div>
</div>
<div class="form-group col-md-6">
{{ form.entrypoint.label_tag }}
<div class="input-group">
{{ form.entrypoint | add_class:"form-control" }}
</div>
</div>
</div>
</div>
<input class="btn btn-primary float-right" type="submit" value="Create webapp">
......
{% extends "base.html" %}
{% load static groups converters humanize %}
{% load static converters humanize %}
{% block title %}Available apps{% endblock %}
......@@ -21,7 +21,7 @@
</div>
<div class="col text-right">
{% if request.user|has_group:'inria' %}
{% if request.user.is_provider %}
<a
href="{% url 'main:webapp_creation' %}"
data-toggle="tooltip"
......
......@@ -46,6 +46,13 @@
{{ form.description | attr:"placeholder:Enter the description of your app here" | add_class:"form-control" }}
</div>
<div class="form-group">
{{ form.entrypoint.label_tag }}
<div class="input-group">
{{ form.entrypoint | add_class:"form-control" }}
</div>
</div>
<div class="form-group">
{{ form.docker_os.label_tag }}
......
......@@ -180,6 +180,9 @@ with env_loader.EnvironmentVarLoader(__name__, "ALLGO_",
env_var("ALLGO_SSH_PORT", default="2222",
help="tcp port where allgo is reachable by ssh")
env_var("ALLGO_ALLOWED_DEVELOPER_DOMAINS",
default="localhost",
help="allowed domains to create applications")
env_var("ALLGO_WEBAPP_DEFAULT_MEMORY_LIMIT_MB", default=str(4*1024),
help="default memory limit (in megabytes) for newly created webapps")
......
......@@ -262,6 +262,7 @@ SOCIALACCOUNT_PROVIDERS = {
},
}
ALLOWED_DEVELOPER_DOMAINS = env.ALLGO_ALLOWED_DEVELOPER_DOMAINS.split(",")
# Logging
# ------------------------------------------------------------------------------
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment