Commit fddee865 by Kálmán Viktor

Merge branch 'master' into feature-gitlabheader

parents 919a6b13 ff91b0ff
...@@ -450,7 +450,7 @@ if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE': ...@@ -450,7 +450,7 @@ if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE':
) )
AUTHENTICATION_BACKENDS = ( AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', 'django.contrib.auth.backends.ModelBackend',
'djangosaml2.backends.Saml2Backend', 'common.backends.Saml2Backend',
) )
remote_metadata = join(SITE_ROOT, 'remote_metadata.xml') remote_metadata = join(SITE_ROOT, 'remote_metadata.xml')
...@@ -528,6 +528,10 @@ except: ...@@ -528,6 +528,10 @@ except:
LOCALE_PATHS = (join(SITE_ROOT, 'locale'), ) LOCALE_PATHS = (join(SITE_ROOT, 'locale'), )
COMPANY_NAME = get_env_variable("COMPANY_NAME", "BME IK 2015") COMPANY_NAME = get_env_variable("COMPANY_NAME", "BME IK 2015")
first, last = get_env_variable(
'VNC_PORT_RANGE', '20000, 65536').replace(' ', '').split(',')
VNC_PORT_RANGE = (int(first), int(last)) # inclusive start, exclusive end
graphite_host = environ.get("GRAPHITE_HOST", None) graphite_host = environ.get("GRAPHITE_HOST", None)
graphite_port = environ.get("GRAPHITE_PORT", None) graphite_port = environ.get("GRAPHITE_PORT", None)
if graphite_host and graphite_port: if graphite_host and graphite_port:
......
...@@ -88,3 +88,4 @@ if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE': ...@@ -88,3 +88,4 @@ if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE':
) )
handler500 = 'common.views.handler500' handler500 = 'common.views.handler500'
handler403 = 'common.views.handler403'
# -*- coding: utf-8 -*-
# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
import re
from djangosaml2.backends import Saml2Backend as Saml2BackendBase
class Saml2Backend(Saml2BackendBase):
u"""
>>> b = Saml2Backend()
>>> b.clean_user_main_attribute(u'Ékezetes Enikő')
u'+00c9kezetes+0020Enik+0151'
>>> b.clean_user_main_attribute(u'Cé++')
u'C+00e9+002b+002b'
>>> b.clean_user_main_attribute(u'test')
u'test'
>>> b.clean_user_main_attribute(u'3+4')
u'3+002b4'
"""
def clean_user_main_attribute(self, main_attribute):
def replace(match):
match = match.group()
return '+%04x' % ord(match)
if isinstance(main_attribute, str):
main_attribute = main_attribute.decode('UTF-8')
assert isinstance(main_attribute, unicode)
return re.sub(r'[^\w.@-]', replace, main_attribute)
def _set_attribute(self, obj, attr, value):
if attr == 'username':
value = self.clean_user_main_attribute(value)
return super(Saml2Backend, self)._set_attribute(obj, attr, value)
...@@ -170,8 +170,8 @@ class Operation(object): ...@@ -170,8 +170,8 @@ class Operation(object):
raise ImproperlyConfigured( raise ImproperlyConfigured(
"Set required_perms to () if none needed.") "Set required_perms to () if none needed.")
if not user.has_perms(cls.required_perms): if not user.has_perms(cls.required_perms):
raise PermissionDenied("%s doesn't have the required permissions." raise PermissionDenied(
% user) u"%s doesn't have the required permissions." % user)
if cls.superuser_required and not user.is_superuser: if cls.superuser_required and not user.is_superuser:
raise humanize_exception(ugettext_noop( raise humanize_exception(ugettext_noop(
"Superuser privileges are required."), PermissionDenied()) "Superuser privileges are required."), PermissionDenied())
......
...@@ -19,32 +19,42 @@ from sys import exc_info ...@@ -19,32 +19,42 @@ from sys import exc_info
import logging import logging
from django.template import RequestContext
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.template import RequestContext
from .models import HumanReadableException from .models import HumanReadableException
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def handler500(request): def get_context(request, exception):
cls, exception, traceback = exc_info()
logger.exception("unhandled exception")
ctx = {} ctx = {}
if isinstance(exception, HumanReadableException): if issubclass(exception.__class__, HumanReadableException):
try: try:
ctx['error'] = exception.get_user_text() if request.user.is_superuser:
ctx['error'] = exception.get_admin_text()
else:
ctx['error'] = exception.get_user_text()
except: except:
pass pass
else: return ctx
try:
if request.user.is_superuser():
ctx['error'] = exception.get_admin_text() def handler500(request):
except: cls, exception, traceback = exc_info()
pass logger.exception("unhandled exception")
ctx = get_context(request, exception)
try: try:
resp = render_to_response("500.html", ctx, RequestContext(request)) resp = render_to_response("500.html", ctx, RequestContext(request))
except: except:
resp = render_to_response("500.html", ctx) resp = render_to_response("500.html", ctx)
resp.status_code = 500 resp.status_code = 500
return resp return resp
def handler403(request):
cls, exception, traceback = exc_info()
ctx = get_context(request, exception)
resp = render_to_response("403.html", ctx)
resp.status_code = 403
return resp
...@@ -309,7 +309,7 @@ if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'): ...@@ -309,7 +309,7 @@ if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'):
attributes = kwargs.pop('attributes') attributes = kwargs.pop('attributes')
atr = settings.SAML_ORG_ID_ATTRIBUTE atr = settings.SAML_ORG_ID_ATTRIBUTE
try: try:
value = attributes[atr][0] value = attributes[atr][0].upper()
except Exception as e: except Exception as e:
value = None value = None
logger.info("save_org_id couldn't find attribute. %s", unicode(e)) logger.info("save_org_id couldn't find attribute. %s", unicode(e))
...@@ -339,7 +339,7 @@ if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'): ...@@ -339,7 +339,7 @@ if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'):
group, unicode(g)) group, unicode(g))
g.user_set.add(sender) g.user_set.add(sender)
for i in FutureMember.objects.filter(org_id=value): for i in FutureMember.objects.filter(org_id__iexact=value):
i.group.user_set.add(sender) i.group.user_set.add(sender)
i.delete() i.delete()
......
...@@ -8,11 +8,13 @@ ...@@ -8,11 +8,13 @@
<a class="btn btn-default" href="{{object.get_absolute_url}}" data-dismiss="modal"> <a class="btn btn-default" href="{{object.get_absolute_url}}" data-dismiss="modal">
{% trans "Cancel" %} {% trans "Cancel" %}
</a> </a>
{% if lease_types %}
<a class="btn btn-primary" id="vm-renew-request-lease-button" <a class="btn btn-primary" id="vm-renew-request-lease-button"
href="{% url "request.views.request-lease" vm_pk=object.pk %}"> href="{% url "request.views.request-lease" vm_pk=object.pk %}">
<i class="fa fa-forward"></i> <i class="fa fa-forward"></i>
{% trans "Request longer lease" %} {% trans "Request longer lease" %}
</a> </a>
{% endif %}
<button class="btn btn-{{ opview.effect }} btn-op-form-send" type="submit" id="op-form-send"> <button class="btn btn-{{ opview.effect }} btn-op-form-send" type="submit" id="op-form-send">
{% if opview.icon %}<i class="fa fa-fw fa-{{opview.icon}}"></i> {% endif %}{{ op.name|capfirst }} {% if opview.icon %}<i class="fa fa-fw fa-{{opview.icon}}"></i> {% endif %}{{ op.name|capfirst }}
</button> </button>
......
...@@ -13,11 +13,11 @@ ...@@ -13,11 +13,11 @@
{% if op.resources_change %} {% if op.resources_change %}
<button type="submit" class="btn btn-success btn-sm change-resources-button" <button type="submit" class="btn btn-success btn-sm change-resources-button"
id="vm-details-resources-save" data-vm="{{ instance.pk }}" id="vm-details-resources-save" data-vm="{{ instance.pk }}"
{% if op.resources_change.disabled %}disabled{% endif %}> {% if not save_resources_enabled %}disabled{% endif %}>
<i class="fa fa-floppy-o"></i> {% trans "Save resources" %} <i class="fa fa-floppy-o"></i> {% trans "Save resources" %}
</button> </button>
<span class="change-resources-help" <span class="change-resources-help"
{% if not op.resources_change.disabled %}style="display: none;"{% endif %}> {% if save_resources_enabled %}style="display: none;"{% endif %}>
{% trans "Stop your VM to change resources." %} {% trans "Stop your VM to change resources." %}
</span> </span>
{% else %} {% else %}
......
...@@ -146,7 +146,7 @@ class GroupDetailView(CheckedDetailView): ...@@ -146,7 +146,7 @@ class GroupDetailView(CheckedDetailView):
self.object.user_set.add(entity) self.object.user_set.add(entity)
except User.DoesNotExist: except User.DoesNotExist:
if saml_available: if saml_available:
FutureMember.objects.get_or_create(org_id=name, FutureMember.objects.get_or_create(org_id=name.upper(),
group=self.object) group=self.object)
else: else:
messages.warning(request, _('User "%s" not found.') % name) messages.warning(request, _('User "%s" not found.') % name)
......
...@@ -70,7 +70,7 @@ def search_user(keyword): ...@@ -70,7 +70,7 @@ def search_user(keyword):
return User.objects.get(username=keyword) return User.objects.get(username=keyword)
except User.DoesNotExist: except User.DoesNotExist:
try: try:
return User.objects.get(profile__org_id=keyword) return User.objects.get(profile__org_id__iexact=keyword)
except User.DoesNotExist: except User.DoesNotExist:
return User.objects.get(email=keyword) return User.objects.get(email=keyword)
......
...@@ -66,7 +66,7 @@ from ..forms import ( ...@@ -66,7 +66,7 @@ from ..forms import (
VmPortRemoveForm, VmPortAddForm, VmPortRemoveForm, VmPortAddForm,
VmRemoveInterfaceForm, VmRemoveInterfaceForm,
) )
from request.models import TemplateAccessType from request.models import TemplateAccessType, LeaseType
from request.forms import LeaseRequestForm, TemplateRequestForm from request.forms import LeaseRequestForm, TemplateRequestForm
from ..models import Favourite from ..models import Favourite
from manager.scheduler import has_traits from manager.scheduler import has_traits
...@@ -173,6 +173,10 @@ class VmDetailView(GraphMixin, CheckedDetailView): ...@@ -173,6 +173,10 @@ class VmDetailView(GraphMixin, CheckedDetailView):
context['is_operator'] = is_operator context['is_operator'] = is_operator
context['is_owner'] = is_owner context['is_owner'] = is_owner
# operation also allows RUNNING (if with_shutdown is present)
context['save_resources_enabled'] = instance.status not in ("RUNNING",
"PENDING")
return context return context
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
...@@ -681,6 +685,7 @@ class VmRenewView(FormOperationMixin, TokenOperationView, VmOperationView): ...@@ -681,6 +685,7 @@ class VmRenewView(FormOperationMixin, TokenOperationView, VmOperationView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(VmRenewView, self).get_context_data(**kwargs) context = super(VmRenewView, self).get_context_data(**kwargs)
context['lease_request_form'] = LeaseRequestForm(request=self.request) context['lease_request_form'] = LeaseRequestForm(request=self.request)
context['lease_types'] = LeaseType.objects.exists()
return context return context
......
...@@ -19,9 +19,11 @@ from __future__ import unicode_literals, absolute_import ...@@ -19,9 +19,11 @@ from __future__ import unicode_literals, absolute_import
from django.views.generic import ( from django.views.generic import (
UpdateView, TemplateView, DetailView, CreateView, FormView, DeleteView, UpdateView, TemplateView, DetailView, CreateView, FormView, DeleteView,
) )
from django.contrib.messages.views import SuccessMessageMixin
from django.shortcuts import redirect, get_object_or_404 from django.shortcuts import redirect, get_object_or_404
from django.core.exceptions import PermissionDenied, SuspiciousOperation from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from braces.views import SuperuserRequiredMixin, LoginRequiredMixin from braces.views import SuperuserRequiredMixin, LoginRequiredMixin
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
...@@ -97,17 +99,19 @@ class RequestDetail(LoginRequiredMixin, DetailView): ...@@ -97,17 +99,19 @@ class RequestDetail(LoginRequiredMixin, DetailView):
class TemplateAccessTypeDetail(LoginRequiredMixin, SuperuserRequiredMixin, class TemplateAccessTypeDetail(LoginRequiredMixin, SuperuserRequiredMixin,
UpdateView): SuccessMessageMixin, UpdateView):
model = TemplateAccessType model = TemplateAccessType
template_name = "request/template-type-form.html" template_name = "request/template-type-form.html"
form_class = TemplateAccessTypeForm form_class = TemplateAccessTypeForm
success_message = _("Template access type successfully updated.")
class TemplateAccessTypeCreate(LoginRequiredMixin, SuperuserRequiredMixin, class TemplateAccessTypeCreate(LoginRequiredMixin, SuperuserRequiredMixin,
CreateView): SuccessMessageMixin, CreateView):
model = TemplateAccessType model = TemplateAccessType
template_name = "request/template-type-form.html" template_name = "request/template-type-form.html"
form_class = TemplateAccessTypeForm form_class = TemplateAccessTypeForm
success_message = _("New template access type successfully created.")
class TemplateAccessTypeDelete(LoginRequiredMixin, SuperuserRequiredMixin, class TemplateAccessTypeDelete(LoginRequiredMixin, SuperuserRequiredMixin,
...@@ -119,16 +123,20 @@ class TemplateAccessTypeDelete(LoginRequiredMixin, SuperuserRequiredMixin, ...@@ -119,16 +123,20 @@ class TemplateAccessTypeDelete(LoginRequiredMixin, SuperuserRequiredMixin,
return reverse("request.views.type-list") return reverse("request.views.type-list")
class LeaseTypeDetail(LoginRequiredMixin, SuperuserRequiredMixin, UpdateView): class LeaseTypeDetail(LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, UpdateView):
model = LeaseType model = LeaseType
template_name = "request/lease-type-form.html" template_name = "request/lease-type-form.html"
form_class = LeaseTypeForm form_class = LeaseTypeForm
success_message = _("Lease type successfully updated.")
class LeaseTypeCreate(LoginRequiredMixin, SuperuserRequiredMixin, CreateView): class LeaseTypeCreate(LoginRequiredMixin, SuperuserRequiredMixin,
SuccessMessageMixin, CreateView):
model = LeaseType model = LeaseType
template_name = "request/lease-type-form.html" template_name = "request/lease-type-form.html"
form_class = LeaseTypeForm form_class = LeaseTypeForm
success_message = _("New lease type successfully created.")
class LeaseTypeDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView): class LeaseTypeDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
...@@ -154,7 +162,7 @@ class RequestTypeList(LoginRequiredMixin, SuperuserRequiredMixin, ...@@ -154,7 +162,7 @@ class RequestTypeList(LoginRequiredMixin, SuperuserRequiredMixin,
return context return context
class TemplateRequestView(FormView): class TemplateRequestView(LoginRequiredMixin, FormView):
form_class = TemplateRequestForm form_class = TemplateRequestForm
template_name = "request/request-template.html" template_name = "request/request-template.html"
...@@ -185,7 +193,7 @@ class TemplateRequestView(FormView): ...@@ -185,7 +193,7 @@ class TemplateRequestView(FormView):
return redirect("/") return redirect("/")
class VmRequestMixin(object): class VmRequestMixin(LoginRequiredMixin, object):
def get_vm(self): def get_vm(self):
return get_object_or_404(Instance, pk=self.kwargs['vm_pk']) return get_object_or_404(Instance, pk=self.kwargs['vm_pk'])
......
{% extends "base.html" %}
{% load i18n %}
{% block title %}HTTP 403{% endblock %}
{% block page_title %}{% trans ":(" %}{% endblock page_title %}
{% block content %}
<div class="alert alert-danger" style="font-size: 22px; margin-top: 2em;">
<div class="row">
<div class="col-md-2" style="text-align: center;">
HTTP 403
</div>
<div class="col-md-10" style="text-align: center;">
{% if error %}
{{ error }}
{% else %}
{% trans "Forbidden" %}
{% endif %}
</div>
</div>
</div>
{% endblock content %}
...@@ -6,5 +6,14 @@ ...@@ -6,5 +6,14 @@
{% block page_title %}{% trans "Page not found" %}{% endblock page_title %} {% block page_title %}{% trans "Page not found" %}{% endblock page_title %}
{% block content %} {% block content %}
<p>{% trans "This page does not exist." %}</p> <div class="alert alert-warning" style="font-size: 22px; margin-top: 2em;">
<div class="row">
<div class="col-md-2" style="text-align: center;">
HTTP 404
</div>
<div class="col-md-10" style="text-align: center;">
{% trans "This page does not exist." %}
</div>
</div>
</div>
{% endblock content %} {% endblock content %}
{% extends "dashboard/base.html" %} {% extends "base.html" %}
{% load i18n %} {% load i18n %}
{% block title %}HTTP 500{% endblock %} {% block title %}HTTP 500{% endblock %}
......
...@@ -62,7 +62,6 @@ scheduler = import_module(name=django.conf.settings.VM_SCHEDULER) ...@@ -62,7 +62,6 @@ scheduler = import_module(name=django.conf.settings.VM_SCHEDULER)
ACCESS_PROTOCOLS = django.conf.settings.VM_ACCESS_PROTOCOLS ACCESS_PROTOCOLS = django.conf.settings.VM_ACCESS_PROTOCOLS
ACCESS_METHODS = [(key, name) for key, (name, port, transport) ACCESS_METHODS = [(key, name) for key, (name, port, transport)
in ACCESS_PROTOCOLS.iteritems()] in ACCESS_PROTOCOLS.iteritems()]
VNC_PORT_RANGE = (20000, 65536) # inclusive start, exclusive end
def find_unused_port(port_range, used_ports=[]): def find_unused_port(port_range, used_ports=[]):
...@@ -81,7 +80,7 @@ def find_unused_port(port_range, used_ports=[]): ...@@ -81,7 +80,7 @@ def find_unused_port(port_range, used_ports=[]):
def find_unused_vnc_port(): def find_unused_vnc_port():
port = find_unused_port( port = find_unused_port(
port_range=VNC_PORT_RANGE, port_range=django.conf.settings.VNC_PORT_RANGE,
used_ports=Instance.objects.values_list('vnc_port', flat=True)) used_ports=Instance.objects.values_list('vnc_port', flat=True))
if port is None: if port is None:
......
...@@ -11,11 +11,12 @@ django-braces==1.4.0 ...@@ -11,11 +11,12 @@ django-braces==1.4.0
django-celery==3.1.16 django-celery==3.1.16
django-crispy-forms==1.4.0 django-crispy-forms==1.4.0
django-model-utils==2.2 django-model-utils==2.2
djangosaml2==0.13.0
django-sizefield==0.6 django-sizefield==0.6
django-sshkey==2.2.0 django-sshkey==2.2.0
django-statici18n==1.1 django-statici18n==1.1
django-tables2==0.15.0 django-tables2==0.15.0
git+https://git.ik.bme.hu/circle/django-taggit.git django-taggit==0.13.0
docutils==0.12 docutils==0.12
Jinja2==2.7.3 Jinja2==2.7.3
jsonfield==1.0.0 jsonfield==1.0.0
...@@ -32,6 +33,7 @@ pyinotify==0.9.4 ...@@ -32,6 +33,7 @@ pyinotify==0.9.4
pytz==2014.7 pytz==2014.7
requests==2.5.3 requests==2.5.3
salt==2014.1.0 salt==2014.1.0
shutilwhich==1.0.1
simplejson==3.6.5 simplejson==3.6.5
six==1.8.0 six==1.8.0
slimit==0.8.1 slimit==0.8.1
......
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 sign in to comment