diff --git a/.gitignore b/.gitignore
index e084584..6a9495f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ local_settings.py
venv
.idea
db.sqlite3
+node_modules
diff --git a/mailadmin/permissions.py b/mailadmin/permissions.py
new file mode 100644
index 0000000..91ff96f
--- /dev/null
+++ b/mailadmin/permissions.py
@@ -0,0 +1,36 @@
+from rest_framework import permissions
+from mailadmin.models import OrgUnit
+import re
+
+from mailadmin.serializers import ForwardSerializer
+
+class IsOwnerOrReadOnly(permissions.BasePermission):
+ """
+ Custom permission to only allow owners of an object to edit it.
+ """
+
+ def has_object_permission(self, request, view, obj):
+ # Read permissions are allowed to any request,
+ # so we'll always allow GET, HEAD or OPTIONS requests.
+ if request.method in permissions.SAFE_METHODS:
+ return True
+
+ # Write permissions are only allowed to the owner
+ return obj.owner == request.user
+
+
+class DestinationPrefixOwner(permissions.BasePermission):
+ def has_permission(self, request, view):
+ fw = ForwardSerializer(data=request.DATA)
+ if not fw.is_valid():
+ return True # Skip auth if not valid data
+
+ my_groups = request.user.groups.all()
+ # Look for mailinglist prefixes
+ my_prefixes = OrgUnit.objects.filter(admin_groups__in=my_groups).distinct().values_list('prefix', flat=True)
+ # Prepare regular expression
+ regexp = '|'.join(['{0}-'.format(p) for p in my_prefixes])
+ if len(my_prefixes) == 0:
+ return False
+ # The destination should have at least one match with the prefix regexp
+ return re.search(regexp, fw.data['dest']) is not None
\ No newline at end of file
diff --git a/mailadmin/serializers.py b/mailadmin/serializers.py
new file mode 100644
index 0000000..3de10b3
--- /dev/null
+++ b/mailadmin/serializers.py
@@ -0,0 +1,40 @@
+from django.conf import settings
+from django.contrib.auth.models import User, Group
+from rest_framework import serializers
+
+from mailadmin.models import OrgUnit
+
+
+class GroupSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Group
+ fields = ['id', 'name']
+
+
+class UserSerializer(serializers.ModelSerializer):
+ groups = GroupSerializer(many=True)
+
+ class Meta:
+ model = User
+ exclude = ['password', 'user_permissions']
+
+
+class OrgUnitSerializer(serializers.ModelSerializer):
+ admin_groups = GroupSerializer(many=True)
+ member_groups = GroupSerializer(many=True)
+
+ class Meta:
+ model = OrgUnit
+
+
+class ForwardSerializer(serializers.Serializer):
+ dest = serializers.EmailField(required=True)
+ forward = serializers.EmailField(required=True)
+ _domain = settings.NEUF_EMAIL_DOMAIN
+
+ def validate_dest(self, attrs, source):
+ domain_part = attrs[source].split('@')[1]
+ if domain_part != self._domain:
+ raise serializers.ValidationError("dest '{0}' is not in domain {1}".format(attrs[source], self._domain))
+
+ return attrs
\ No newline at end of file
diff --git a/mailadmin/templates/base.html b/mailadmin/templates/base.html
index f685cea..cb94599 100644
--- a/mailadmin/templates/base.html
+++ b/mailadmin/templates/base.html
@@ -1,8 +1,11 @@
-
+{% load staticfiles %}
+
{% block "title" %}{% endblock %}
+
+
{% block "body" %}{% endblock %}
diff --git a/mailadmin/templates/home.html b/mailadmin/templates/home.html
index 7254313..796b39b 100644
--- a/mailadmin/templates/home.html
+++ b/mailadmin/templates/home.html
@@ -1,4 +1,10 @@
{% extends "base.html" %}
+{% load bootstrap %}
{% block "body" %}
-Hi, you should try the APIS
+ {% if not user.is_authenticated %}
+ {{ form|bootstrap }}
+ {% else %}
+ Hei, {{ user.username }}
+ {% endif %}
+ API.
{% endblock %}
\ No newline at end of file
diff --git a/mailadmin/tests.py b/mailadmin/tests.py
index d1154cd..05373d8 100644
--- a/mailadmin/tests.py
+++ b/mailadmin/tests.py
@@ -36,9 +36,9 @@ class APITest(TestCase):
@unittest.skip("Not now")
def test_delforward(self):
server = CPanel()
- params = dict(
- forwarder='test.testesen@studentersamfundet.no=nikolaik@gmail.com',
- )
+ params = {
+ 'arg-0': 'kak-lol@studentersamfundet.no=nikolaik@gmail.com',
+ }
res = server.api('Email', 'delforward', params, version=1)
# FIXME: Does not work!
self.assertIsInstance(res, dict)
diff --git a/mailadmin/urls.py b/mailadmin/urls.py
index d6cdc19..9a510fe 100644
--- a/mailadmin/urls.py
+++ b/mailadmin/urls.py
@@ -1,6 +1,16 @@
-from mailadmin.views import ForwardsViewSet
+from django.conf.urls import patterns, url
from rest_framework.routers import DefaultRouter
+from mailadmin.views import ForwardsViewSet, MyUserViewSet, OrgUnitViewSet, ForwardCreateView, ForwardDeleteView
+
router = DefaultRouter()
router.register(r'forwards', ForwardsViewSet, base_name='all')
+router.register(r'me', MyUserViewSet, base_name='me')
+router.register(r'orgunits', OrgUnitViewSet, base_name='ous')
urlpatterns = router.urls
+
+urlpatterns += patterns(
+ '',
+ url(r'^forward/$', ForwardCreateView.as_view(), name='forward-create'),
+ url(r'^forward/(?P.+)/$', ForwardDeleteView.as_view(), name='forward-delete'),
+)
diff --git a/mailadmin/views.py b/mailadmin/views.py
index 6beaa7d..7fb68c3 100644
--- a/mailadmin/views.py
+++ b/mailadmin/views.py
@@ -1,32 +1,120 @@
-from django.contrib.auth.models import Group
+from django.conf import settings
+from django.contrib.auth import login
+from django.contrib.auth.forms import AuthenticationForm
+from django.contrib.auth.models import User
from django.shortcuts import render
-from rest_framework import viewsets
+from rest_framework import viewsets, views
+from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from mailadmin.api import CPanel
from mailadmin.models import OrgUnit
+from mailadmin.serializers import UserSerializer, OrgUnitSerializer, ForwardSerializer
+from mailadmin.permissions import DestinationPrefixOwner
def home(request):
- return render(request, "home.html")
+ form = {}
+ if request.method == "POST":
+ form = AuthenticationForm(data=request.POST)
+ if form.is_valid():
+ # Log the user in.
+ login(request, form.get_user())
+ else:
+ if not request.user.is_authenticated():
+ form = AuthenticationForm(request)
+
+ return render(request, "home.html", {'form': form})
class ForwardsViewSet(viewsets.ViewSet):
"""
- A simple ViewSet that for listing forwards
+ Forwards from cPanel
"""
+
+ permission_classes = [IsAuthenticated]
+
def list(self, request):
+ group_filter = request.QUERY_PARAMS.get('groups', None)
+ if group_filter is None:
+ my_groups = request.user.groups.all()
+ else:
+ # Filter groups
+ if ',' in group_filter:
+ group_filter = group_filter.split(',')
+ else:
+ group_filter = [group_filter]
+ my_groups = request.user.groups.filter(pk__in=group_filter)
+
+ # Look for mailinglist prefixes
+ my_prefixes = OrgUnit.objects.filter(admin_groups__in=my_groups).distinct().values_list('prefix', flat=True)
+ # Prepare regular expression
+ regexp = '|'.join(['{0}-'.format(p) for p in my_prefixes])
+
+ if len(my_prefixes) == 0:
+ return Response({'result': 'No results'}) # Need at least one
+
cp = CPanel()
- regexp = self.request.QUERY_PARAMS.get('regexp', None)
- my_groups = request.user.groups.values_list('pk', flat=True)
- print my_groups
- ous = OrgUnit.objects.filter(admin_groups__in=my_groups).values_list('prefix', flat=True)
- ous = set(ous)
- print ous
- #regexp = 'kak-*'
params = dict(
- domain='studentersamfundet.no',
+ domain=settings.NEUF_EMAIL_DOMAIN,
regex=regexp
)
+
res = cp.api('Email', 'listforwards', params)
- return Response(res)
\ No newline at end of file
+ return Response(res)
+
+
+class ForwardCreateView(views.APIView):
+ _cp = CPanel()
+ permission_classes = (DestinationPrefixOwner,)
+
+ def post(self, request):
+ fw = ForwardSerializer(data=request.DATA)
+ if not fw.is_valid():
+ return Response(fw.errors, status=400)
+
+ # fw.data
+ params = dict(
+ domain=settings.NEUF_EMAIL_DOMAIN,
+ email=fw.data['dest'].replace('@{0}'.format(settings.NEUF_EMAIL_DOMAIN), ''),
+ fwdopt='fwd',
+ fwdemail=fw.data['forward']
+ )
+ res = self._cp.api('Email', 'addforward', params)
+ return Response(res)
+
+
+class ForwardDeleteView(views.APIView):
+ _cp = CPanel()
+
+ def delete(self, request, forwarder=None):
+ invalid_forwarder = {'error': 'Invalid forwarder: {0}'.format(forwarder)}
+ if '=' not in forwarder:
+ return Response(invalid_forwarder, status=400)
+
+ fwd_data = forwarder.split("=")
+
+ fw = ForwardSerializer(data={'dest': fwd_data[0], 'forward': fwd_data[1]})
+ if not fw.is_valid():
+ return Response(fw.errors, status=400)
+
+ params = {
+ 'arg-0': '{0}={1}'.format(fw.data['dest'], fw.data['forward'])
+ }
+ res = self._cp.api('Email', 'delforward', params, version=1)
+ return Response(res)
+
+
+class MyUserViewSet(viewsets.ReadOnlyModelViewSet):
+ queryset = User.objects.none()
+ serializer_class = UserSerializer
+ permission_classes = [IsAuthenticated]
+
+ def get_queryset(self):
+ return User.objects.filter(pk=self.request.user.pk)
+
+
+class OrgUnitViewSet(viewsets.ReadOnlyModelViewSet):
+ queryset = OrgUnit.objects.all()
+ serializer_class = OrgUnitSerializer
+ permission_classes = [IsAuthenticated]
\ No newline at end of file
diff --git a/mailadminsite/settings.py b/mailadminsite/settings.py
index 7000f2d..431432f 100644
--- a/mailadminsite/settings.py
+++ b/mailadminsite/settings.py
@@ -37,6 +37,7 @@ INSTALLED_APPS = (
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
+ 'bootstrapform',
'mailadmin',
)
@@ -83,10 +84,14 @@ USE_TZ = True
STATIC_URL = '/static/'
+LOGIN_REDIRECT_URL = '/'
+
CP_API_URL = 'https://web.studentersamfundet.no:2083/json-api/cpanel'
CP_API_USERNAME = "root"
CP_API_PASSWORD = ""
+NEUF_EMAIL_DOMAIN = 'studentersamfundet.no'
+
AUTHENTICATION_BACKENDS = (
'mailadmin.backends.LDAPEmailBackend',
'mailadmin.backends.LDAPUsernameBackend',
@@ -147,12 +152,12 @@ AUTH_LDAP_E_USER_ATTR_MAP = AUTH_LDAP_U_USER_ATTR_MAP
AUTH_LDAP_E_PROFILE_ATTR_MAP = AUTH_LDAP_U_PROFILE_ATTR_MAP
AUTH_LDAP_E_ALWAYS_UPDATE_USER = AUTH_LDAP_U_ALWAYS_UPDATE_USER
-# Debug logging
-import logging
-logger = logging.getLogger('django_auth_ldap')
-logger.addHandler(logging.StreamHandler())
-logger.setLevel(logging.DEBUG)
-
+REST_FRAMEWORK = {
+ 'DEFAULT_AUTHENTICATION_CLASSES': (
+ 'rest_framework.authentication.BasicAuthentication',
+ 'rest_framework.authentication.SessionAuthentication',
+ )
+}
# Local settings
try:
diff --git a/mailadminsite/urls.py b/mailadminsite/urls.py
index d60d462..2a4c36d 100644
--- a/mailadminsite/urls.py
+++ b/mailadminsite/urls.py
@@ -1,5 +1,6 @@
from django.conf.urls import patterns, include, url
from django.contrib import admin
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns = patterns(
'',
@@ -8,3 +9,5 @@ urlpatterns = patterns(
url(r'^admin/', include(admin.site.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
)
+
+urlpatterns += staticfiles_urlpatterns()
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 8d5da09..e9a9b74 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,3 +5,4 @@ django-filter==0.8
djangorestframework==2.4.4
python-ldap==2.4.18
requests==2.4.3
+django-bootstrap-form==3.1