Functional API

This commit is contained in:
nikolark committed 2014-11-09 17:34:15 +01:00
1 parent 32ee67eaa9
commit 81fe4a6872
11 files changed
+218 -25

No files matched your search

+1
View File
@@ -4,3 +4,4 @@ local_settings.py
venv
.idea
db.sqlite3
node_modules
+36
View File
@@ -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
+40
View File
@@ -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
+4 -1
View File
@@ -1,8 +1,11 @@
<!DOCTYPE html>
{% load staticfiles %}<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block "title" %}{% endblock %}</title>
<link rel="stylesheet" href="{% static 'dist/styles/main.css' %}" type="text/css">
<script type="text/javascript" src="{% static 'bower_components/jquery/dist/jquery.js' %}"></script>
</head>
<body>
{% block "body" %}{% endblock %}
+7 -1
View File
@@ -1,4 +1,10 @@
{% extends "base.html" %}
{% load bootstrap %}
{% block "body" %}
Hi, you should try the <a href="/api/">APIS</a>
{% if not user.is_authenticated %}
{{ form|bootstrap }}
{% else %}
<div class="greeting">Hei, {{ user.username }}</div>
{% endif %}
<div></div><a href="/api/">API</a></div>.
{% endblock %}
+3 -3
View File
@@ -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)
+11 -1
View File
@@ -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<forwarder>.+)/$', ForwardDeleteView.as_view(), name='forward-delete'),
)
+101 -13
View File
@@ -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)
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]
+11 -6
View File
@@ -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:
+3
View File
@@ -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()
+1
View File
@@ -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