diff --git a/fabfile.py b/fabfile.py new file mode 100644 index 0000000..c782aa1 --- /dev/null +++ b/fabfile.py @@ -0,0 +1,63 @@ +import logging +import sys + +from contextlib import contextmanager as _contextmanager +from fabric.api import run, sudo, env, cd, prefix, lcd +from fabric.api import local as lrun + +env.use_ssh_config = True +env.hosts = ['dreamcast.neuf.no'] +env.project_path = '/var/www/neuf.no/mailadmin' +env.user = 'gitdeploy' +env.activate = 'source {}/venv/bin/activate'.format(env.project_path) + + +@_contextmanager +def virtualenv(): + with cd(env.project_path): + with prefix(env.activate): + yield + + +def __git_validate_clean(): + with lcd(env.project_path): + if lrun('git diff-files', capture=True) != '': + logging.error("Your git-tree isn't clean! Commit your changes before deploying.") + sys.exit(1) + + +def test(): + lrun("python manage.py test dpdapi") + + +def commit(): + lrun("git add -p && git commit") + + +def push(): + lrun("git push") + + +def prepare_deploy(): + test() + commit() + push() + + +def gitstatus(): + # __git_validate_clean() + with virtualenv(): + # Get source + run('git branch -v') + + +def deploy(): + # __git_validate_clean() + with virtualenv(): + run('git pull') # Get source + run('pip install -r requirements.txt') # install deps in virtualenv + run('python manage.py collectstatic --noinput') # Collect static + run('python manage.py migrate') # Run DB migrations + + # Reload gunicorn + sudo('/etc/init.d/gunicorn reload', shell=False) diff --git a/mailadmin/api.py b/mailadmin/api.py index 39ace75..f3ee196 100644 --- a/mailadmin/api.py +++ b/mailadmin/api.py @@ -2,33 +2,47 @@ import requests from django.conf import settings -class CPanel(object): +class DjangoPostfixDovecotAPI(object): """ - CPanel API + django-postfix-dovecot-api + + https://git.neuf.no/edb/django-postfix-dovecot-api#tab-readme - https://documentation.cpanel.net/display/SDK/cPanel+API+2+-+Email - https://documentation.cpanel.net/display/SDK/cPanel+API+1+-+Email """ - def __init__(self, base_url=settings.CP_API_URL, username=settings.CP_API_USERNAME, password=settings.CP_API_PASSWORD): + def __init__(self, base_url=settings.DPD_API_URL, username=settings.DPD_API_USERNAME, password=settings.DPD_API_PASSWORD): self.base_url = base_url self.username = username self.password = password - def api(self, module, function, params=None, version=2): - generic = { - 'cpanel_jsonapi_user': self.username, - 'cpanel_jsonapi_module': module, - 'cpanel_jsonapi_func': function, - 'cpanel_jsonapi_apiversion': version, - } - if params is not None: - params = dict(generic.items() + params.items()) - + def _api(self, method, path, params=None, json=None): headers = {'content-type': 'application/json'} - r = requests.get( - self.base_url, + url = "{}{}".format(self.base_url, path) + + r = requests.request( + method, + url, params=params, - headers=headers, auth=requests.auth.HTTPBasicAuth(self.username, self.password), - verify=False) - return r.json() \ No newline at end of file + json=json, + headers=headers) + + return r.json() + + def create_aliases(self, aliases): + return self._api('POST', '/aliases/create_bulk/', json=aliases) + + def delete_aliases(self, aliases): + return self._api('DELETE', '/aliases/delete_bulk/', json=aliases) + + def list_aliases_regex(self, regex): + """ Limit aliases by regular expression and domain name """ + params = { + 'domain__name': settings.NEUF_EMAIL_DOMAIN_NAME, + 'source_regex': regex + } + print params + return self._api( + 'GET', + '/aliases/', + params=params + ) \ No newline at end of file diff --git a/mailadmin/permissions.py b/mailadmin/permissions.py index d3337fc..0ad7da1 100644 --- a/mailadmin/permissions.py +++ b/mailadmin/permissions.py @@ -2,33 +2,22 @@ from rest_framework import permissions from mailadmin.models import OrgUnit import re -from mailadmin.serializers import ForwardSerializer +from mailadmin.serializers import AliasSerializer -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): +class SourcePrefixOwner(permissions.BasePermission): # Ref: http://www.django-rest-framework.org/api-guide/permissions # TODO have no idea if this works def has_permission(self, request, view): + # Read permissions (GET, HEAD or OPTIONS) are allowed for all authenticated request + if request.method in permissions.SAFE_METHODS and request.user and request.user.is_authenticated(): + return True + # TODO test handling the DELETE requests if request.user.is_superuser: return True - if request.method == 'DELETE': + if request.method in ['DELETE', 'POST']: print view print view.kwargs forwarder = view.kwargs['forwarder'] # TODO you are here @@ -37,9 +26,9 @@ class DestinationPrefixOwner(permissions.BasePermission): return True fwd_data = forwarder.split("=") - fw = ForwardSerializer(data={'dest': fwd_data[0], 'forward': fwd_data[1]}) + fw = AliasSerializer(data={'dest': fwd_data[0], 'forward': fwd_data[1]}) else: - fw = ForwardSerializer(data=request.DATA) + fw = AliasSerializer(data=request.DATA) if not fw.is_valid(): return True # Skip auth if not valid data diff --git a/mailadmin/serializers.py b/mailadmin/serializers.py index 3de10b3..7422923 100644 --- a/mailadmin/serializers.py +++ b/mailadmin/serializers.py @@ -27,14 +27,14 @@ class OrgUnitSerializer(serializers.ModelSerializer): model = OrgUnit -class ForwardSerializer(serializers.Serializer): - dest = serializers.EmailField(required=True) - forward = serializers.EmailField(required=True) - _domain = settings.NEUF_EMAIL_DOMAIN +class AliasSerializer(serializers.Serializer): + source = serializers.EmailField(required=True) + destination = serializers.EmailField(required=True) + _domain_name = settings.NEUF_EMAIL_DOMAIN_NAME - def validate_dest(self, attrs, source): + def validate_destonation(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)) + if domain_part != self._domain_name: + raise serializers.ValidationError("dest '{0}' is not in domain {1}".format(attrs[source], self._domain_name)) return attrs \ No newline at end of file diff --git a/mailadmin/static/app/scripts/main.js b/mailadmin/static/app/scripts/main.js index 7fb3180..c786ff7 100644 --- a/mailadmin/static/app/scripts/main.js +++ b/mailadmin/static/app/scripts/main.js @@ -65,9 +65,7 @@ $(document).ready(function() { var csrf_token = $('meta[name=x-csrf-token]').attr('content'); var api_urls = { - fwd_list: '/api/forwards/', - fwd_delete: '/api/forward/', - fwd_create: '/api/forward/', // + forwarder + aliases: '/api/aliases/', ou_list: '/api/orgunits/', me: '/api/me/' }; @@ -150,7 +148,7 @@ }); $.post( - api_urls.fwd_create, + api_urls.aliases, new_list[dest], function() { notify('Lagt til', 'Ny liste: ' + dest + 'lagt til', 'success'); @@ -190,22 +188,24 @@ }); /* Load forwards from API and put on page */ - $.getJSON(api_urls.fwd_list, function(data) { - if( !data.cpanelresult ) { - notify("Ingen lister.", 'Fant ingen tilhørende lister', 'info'); + $.getJSON(api_urls.aliases, function(data) { + var err_msg; + if( data && data.length === 0 ) { + err_msg = 'Fant ingen tilhørende lister'; + notify("Ingen lister.", err_msg, 'info'); + forwards_container.html(err_msg); return; } - if( 'error' in data.cpanelresult ) { - notify('Oops!', 'Klarte ikke hente epostlister fra APIet: '+data.cpanelresult.error, 'error'); + if( data && 'error' in data ) { + err_msg = 'Klarte ikke hente epostlister fra APIet: '+data.error; + notify('Oops!', err_msg, 'error'); + forwards_container.html(err_msg); return; } - var forwards = data.cpanelresult.data; - var lists =_.groupBy(forwards, function(fwd) { return fwd.dest; }); - var fw_html = nunjucks.render('list.html', { - lists: lists, - api_urls: api_urls - }); + var forwards = data; + var lists =_.groupBy(forwards, function(fwd) { return fwd.source; }); + var fw_html = nunjucks.render('list.html', { lists: lists }); forwards_container.html(fw_html); @@ -248,7 +248,7 @@ var data = { type: 'DELETE', headers: {'X-CSRFToken': csrf_token}, - url: api_urls.fwd_delete, + url: api_urls.aliases, data: delete_these, dataType: 'json' }; diff --git a/mailadmin/static/app/templates/list.html b/mailadmin/static/app/templates/list.html index 9721531..ed8157c 100644 --- a/mailadmin/static/app/templates/list.html +++ b/mailadmin/static/app/templates/list.html @@ -1,11 +1,11 @@ -{% for list,members in lists %} +{% for list,aliases in lists %}
| {{ member.forward }} | |
| {{ member.destination }} | |
| diff --git a/mailadmin/static/dist/scripts/main.js b/mailadmin/static/dist/scripts/main.js index 7fb3180..c786ff7 100644 --- a/mailadmin/static/dist/scripts/main.js +++ b/mailadmin/static/dist/scripts/main.js @@ -65,9 +65,7 @@ $(document).ready(function() { var csrf_token = $('meta[name=x-csrf-token]').attr('content'); var api_urls = { - fwd_list: '/api/forwards/', - fwd_delete: '/api/forward/', - fwd_create: '/api/forward/', // + forwarder + aliases: '/api/aliases/', ou_list: '/api/orgunits/', me: '/api/me/' }; @@ -150,7 +148,7 @@ }); $.post( - api_urls.fwd_create, + api_urls.aliases, new_list[dest], function() { notify('Lagt til', 'Ny liste: ' + dest + 'lagt til', 'success'); @@ -190,22 +188,24 @@ }); /* Load forwards from API and put on page */ - $.getJSON(api_urls.fwd_list, function(data) { - if( !data.cpanelresult ) { - notify("Ingen lister.", 'Fant ingen tilhørende lister', 'info'); + $.getJSON(api_urls.aliases, function(data) { + var err_msg; + if( data && data.length === 0 ) { + err_msg = 'Fant ingen tilhørende lister'; + notify("Ingen lister.", err_msg, 'info'); + forwards_container.html(err_msg); return; } - if( 'error' in data.cpanelresult ) { - notify('Oops!', 'Klarte ikke hente epostlister fra APIet: '+data.cpanelresult.error, 'error'); + if( data && 'error' in data ) { + err_msg = 'Klarte ikke hente epostlister fra APIet: '+data.error; + notify('Oops!', err_msg, 'error'); + forwards_container.html(err_msg); return; } - var forwards = data.cpanelresult.data; - var lists =_.groupBy(forwards, function(fwd) { return fwd.dest; }); - var fw_html = nunjucks.render('list.html', { - lists: lists, - api_urls: api_urls - }); + var forwards = data; + var lists =_.groupBy(forwards, function(fwd) { return fwd.source; }); + var fw_html = nunjucks.render('list.html', { lists: lists }); forwards_container.html(fw_html); @@ -248,7 +248,7 @@ var data = { type: 'DELETE', headers: {'X-CSRFToken': csrf_token}, - url: api_urls.fwd_delete, + url: api_urls.aliases, data: delete_these, dataType: 'json' }; diff --git a/mailadmin/static/dist/scripts/templates.js b/mailadmin/static/dist/scripts/templates.js index af85147..e29dc63 100644 --- a/mailadmin/static/dist/scripts/templates.js +++ b/mailadmin/static/dist/scripts/templates.js @@ -40,7 +40,7 @@ for(t_1=0; t_1 < t_3.length; t_1++) { var t_4 = t_3[t_1][0] frame.set("list", t_3[t_1][0]); var t_5 = t_3[t_1][1] -frame.set("members", t_3[t_1][1]); +frame.set("aliases", t_3[t_1][1]); frame.set("loop.index", t_1 + 1); frame.set("loop.index0", t_1); frame.set("loop.revindex", t_2 - t_1); @@ -69,19 +69,19 @@ frame.set("loop.first", t_6 === 0); frame.set("loop.last", t_6 === t_7 - 1); frame.set("loop.length", t_7); output += "\n\t\t\t\t | |
| "; -output += runtime.suppressValue(runtime.memberLookup((t_9),"forward", env.autoesc), env.autoesc); +output += runtime.suppressValue(runtime.memberLookup((t_9),"destination", env.autoesc), env.autoesc); output += " | |
| "; -output += runtime.suppressValue(runtime.memberLookup((t_15),"forward", env.autoesc), env.autoesc); +output += runtime.suppressValue(runtime.memberLookup((t_15),"destination", env.autoesc), env.autoesc); output += " |