Mailbox tests passing
This commit is contained in:
parent
6240fa3139
commit
9082770642
3
TODO.md
3
TODO.md
|
@ -163,3 +163,6 @@ APPS app?
|
|||
|
||||
|
||||
* pip upgrade or install
|
||||
|
||||
|
||||
* disable account triggers save on cascade to execute backends save(update_field=[])
|
||||
|
|
|
@ -111,14 +111,15 @@ class AdminPasswordChangeForm(forms.Form):
|
|||
if password:
|
||||
self.user.set_password(password)
|
||||
if commit:
|
||||
self.user.save()
|
||||
self.user.save(update_fields=['password'])
|
||||
for ix, rel in enumerate(self.related):
|
||||
password = self.cleaned_data['password1_%s' % ix]
|
||||
if password:
|
||||
print password
|
||||
set_password = getattr(rel, 'set_password')
|
||||
set_password(password)
|
||||
if commit:
|
||||
rel.save()
|
||||
rel.save(update_fields=['password'])
|
||||
return self.user
|
||||
|
||||
def _get_changed_data(self):
|
||||
|
|
|
@ -33,28 +33,28 @@ def get_modeladmin(model, import_module=True):
|
|||
|
||||
def insertattr(model, name, value, weight=0):
|
||||
""" Inserts attribute to a modeladmin """
|
||||
modeladmin = model
|
||||
modeladmin_class = model
|
||||
if models.Model in model.__mro__:
|
||||
modeladmin = type(get_modeladmin(model))
|
||||
modeladmin_class = type(get_modeladmin(model))
|
||||
# Avoid inlines defined on parent class be shared between subclasses
|
||||
# Seems that if we use tuples they are lost in some conditions like changing
|
||||
# the tuple in modeladmin.__init__
|
||||
if not getattr(modeladmin, name):
|
||||
setattr(type(modeladmin), name, [])
|
||||
if not getattr(modeladmin_class, name):
|
||||
setattr(modeladmin_class, name, [])
|
||||
|
||||
inserted_attrs = getattr(modeladmin, '__inserted_attrs__', {})
|
||||
inserted_attrs = getattr(modeladmin_class, '__inserted_attrs__', {})
|
||||
if not name in inserted_attrs:
|
||||
weights = {}
|
||||
if hasattr(modeladmin, 'weights') and name in modeladmin.weights:
|
||||
weights = modeladmin.weights.get(name)
|
||||
if hasattr(modeladmin_class, 'weights') and name in modeladmin_class.weights:
|
||||
weights = modeladmin_class.weights.get(name)
|
||||
inserted_attrs[name] = [
|
||||
(attr, weights.get(attr, 0)) for attr in getattr(modeladmin, name)
|
||||
(attr, weights.get(attr, 0)) for attr in getattr(modeladmin_class, name)
|
||||
]
|
||||
|
||||
inserted_attrs[name].append((value, weight))
|
||||
inserted_attrs[name].sort(key=lambda a: a[1])
|
||||
setattr(modeladmin, name, [ attr[0] for attr in inserted_attrs[name] ])
|
||||
setattr(modeladmin, '__inserted_attrs__', inserted_attrs)
|
||||
setattr(modeladmin_class, name, [ attr[0] for attr in inserted_attrs[name] ])
|
||||
setattr(modeladmin_class, '__inserted_attrs__', inserted_attrs)
|
||||
|
||||
|
||||
def wrap_admin_view(modeladmin, view):
|
||||
|
|
|
@ -9,10 +9,13 @@ class SetPasswordApiMixin(object):
|
|||
@action(serializer_class=SetPasswordSerializer)
|
||||
def set_password(self, request, pk):
|
||||
obj = self.get_object()
|
||||
serializer = SetPasswordSerializer(data=request.DATA)
|
||||
data = request.DATA
|
||||
if isinstance(data, basestring):
|
||||
data = {'password': data}
|
||||
serializer = SetPasswordSerializer(data=data)
|
||||
if serializer.is_valid():
|
||||
obj.set_password(serializer.data['password'])
|
||||
obj.save()
|
||||
obj.save(update_fields=['password'])
|
||||
return Response({'status': 'password changed'})
|
||||
else:
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.module_loading import autodiscover_modules
|
||||
from rest_framework.routers import DefaultRouter, Route, flatten, replace_methodname
|
||||
|
||||
from orchestra import settings
|
||||
from orchestra.utils.apps import autodiscover as module_autodiscover
|
||||
#from orchestra.utils.apps import autodiscover as module_autodiscover
|
||||
from orchestra.utils.python import import_class
|
||||
|
||||
from .helpers import insert_links, replace_collectionmethodname
|
||||
|
@ -99,16 +100,16 @@ class LinkHeaderRouter(DefaultRouter):
|
|||
def insert(self, prefix_or_model, name, field, **kwargs):
|
||||
""" Dynamically add new fields to an existing serializer """
|
||||
viewset = self.get_viewset(prefix_or_model)
|
||||
setattr(viewset, 'inserted', getattr(viewset, 'inserted', []))
|
||||
# setattr(viewset, 'inserted', getattr(viewset, 'inserted', []))
|
||||
if viewset.serializer_class is None:
|
||||
viewset.serializer_class = viewset().get_serializer_class()
|
||||
viewset.serializer_class.base_fields.update({name: field(**kwargs)})
|
||||
if not name in viewset.inserted:
|
||||
viewset.serializer_class.Meta.fields += (name,)
|
||||
viewset.inserted.append(name)
|
||||
# if not name in viewset.inserted:
|
||||
viewset.serializer_class.Meta.fields += (name,)
|
||||
# viewset.inserted.append(name)
|
||||
|
||||
|
||||
# Create a router and register our viewsets with it.
|
||||
router = LinkHeaderRouter()
|
||||
|
||||
autodiscover = lambda: (module_autodiscover('api'), module_autodiscover('serializers'))
|
||||
autodiscover = lambda: (autodiscover_modules('api'), autodiscover_modules('serializers'))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from rest_framework import viewsets
|
||||
|
||||
from orchestra.api import router
|
||||
from orchestra.api import router, SetPasswordApiMixin
|
||||
|
||||
from .models import Account
|
||||
from .serializers import AccountSerializer
|
||||
|
@ -12,7 +12,7 @@ class AccountApiMixin(object):
|
|||
return qs.filter(account=self.request.user.pk)
|
||||
|
||||
|
||||
class AccountViewSet(viewsets.ModelViewSet):
|
||||
class AccountViewSet(SetPasswordApiMixin, viewsets.ModelViewSet):
|
||||
model = Account
|
||||
serializer_class = AccountSerializer
|
||||
singleton_pk = lambda _,request: request.user.pk
|
||||
|
|
|
@ -42,7 +42,7 @@ class PermissionInline(AccountAdminMixin, admin.TabularInline):
|
|||
""" Make value input widget bigger """
|
||||
formfield = super(PermissionInline, self).formfield_for_dbfield(db_field, **kwargs)
|
||||
if db_field.name == 'database':
|
||||
# Hack widget render in order to append ?account=id to the add url
|
||||
# Hack widget render in order to append ?type='db_type' to the add url
|
||||
db_type = self.parent_object.type
|
||||
old_render = formfield.widget.render
|
||||
def render(*args, **kwargs):
|
||||
|
|
|
@ -6,7 +6,7 @@ from orchestra.apps.resources import ServiceMonitor
|
|||
from . import settings
|
||||
|
||||
|
||||
class MySQLDBBackend(ServiceController):
|
||||
class MySQLBackend(ServiceController):
|
||||
verbose_name = "MySQL database"
|
||||
model = 'databases.Database'
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class Database(models.Model):
|
|||
|
||||
@property
|
||||
def owner(self):
|
||||
self.users.get(is_owner=True)
|
||||
return self.roles.get(is_owner=True).user
|
||||
|
||||
|
||||
class Role(models.Model):
|
||||
|
@ -52,6 +52,11 @@ class Role(models.Model):
|
|||
if self.user.type != self.database.type:
|
||||
msg = _("Database and user type doesn't match")
|
||||
raise validators.ValidationError(msg)
|
||||
roles = self.database.roles.values('id')
|
||||
print roles
|
||||
if not roles or (len(roles) == 1 and roles[0].id == self.id):
|
||||
print 'seld'
|
||||
self.is_owner = True
|
||||
|
||||
|
||||
class DatabaseUser(models.Model):
|
||||
|
|
|
@ -14,7 +14,7 @@ class UserSerializer(serializers.HyperlinkedModelSerializer):
|
|||
fields = ('user', 'is_owner',)
|
||||
|
||||
|
||||
class PermissionSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class RoleSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = Role
|
||||
fields = ('database', 'is_owner',)
|
||||
|
@ -32,9 +32,9 @@ class DatabaseUserSerializer(AccountSerializerMixin, serializers.HyperlinkedMode
|
|||
password = serializers.CharField(max_length=128, label=_('Password'),
|
||||
validators=[validate_password], write_only=True,
|
||||
widget=widgets.PasswordInput)
|
||||
permission = PermissionSerializer(source='roles', many=True)
|
||||
roles = RoleSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = DatabaseUser
|
||||
fields = ('url', 'username', 'password', 'type', 'permission')
|
||||
fields = ('url', 'username', 'password', 'type', 'roles')
|
||||
write_only_fields = ('username',)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#import MySQLdb
|
||||
import MySQLdb
|
||||
import os
|
||||
from functools import partial
|
||||
|
||||
from django.conf import settings as djsettings
|
||||
|
@ -9,21 +10,22 @@ from selenium.webdriver.support.select import Select
|
|||
from orchestra.apps.accounts.models import Account
|
||||
from orchestra.apps.orchestration.models import Server, Route
|
||||
from orchestra.utils.system import run
|
||||
from orchestra.utils.tests import BaseLiveServerTestCase, random_ascii
|
||||
from orchestra.utils.tests import (BaseLiveServerTestCase, random_ascii, save_response_on_error,
|
||||
snapshot_on_error)
|
||||
|
||||
from ... import backends, settings
|
||||
from ...models import Database
|
||||
|
||||
|
||||
class DatabaseTestMixin(object):
|
||||
MASTER_ADDR = 'localhost'
|
||||
MASTER_SERVER = os.environ.get('ORCHESTRA_SECOND_SERVER', 'localhost')
|
||||
DEPENDENCIES = (
|
||||
'orchestra.apps.orchestration',
|
||||
'orcgestra.apps.databases',
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super(SystemUserMixin, self).setUp()
|
||||
super(DatabaseTestMixin, self).setUp()
|
||||
self.add_route()
|
||||
djsettings.DEBUG = True
|
||||
|
||||
|
@ -49,28 +51,79 @@ class DatabaseTestMixin(object):
|
|||
raise NotImplementedError
|
||||
|
||||
def test_add(self):
|
||||
self.add()
|
||||
dbname = '%s_database' % random_ascii(5)
|
||||
username = '%s_dbuser' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(dbname, username, password)
|
||||
self.validate_create_table(dbname, username, password)
|
||||
|
||||
|
||||
|
||||
|
||||
class MysqlBackendMixin(object):
|
||||
class MySQLBackendMixin(object):
|
||||
db_type = 'mysql'
|
||||
|
||||
def add_route(self):
|
||||
server = Server.objects.create(name=self.MASTER_ADDR)
|
||||
backend = backends.MysqlBackend.get_name()
|
||||
Route.objects.create(backend=backend, match="database.type == 'mysql'", host=server)
|
||||
server = Server.objects.create(name=self.MASTER_SERVER)
|
||||
backend = backends.MySQLBackend.get_name()
|
||||
match = "database.type == '%s'" % self.db_type
|
||||
Route.objects.create(backend=backend, match=match, host=server)
|
||||
match = "databaseuser.type == '%s'" % self.db_type
|
||||
backend = backends.MySQLUserBackend.get_name()
|
||||
Route.objects.create(backend=backend, match=match, host=server)
|
||||
|
||||
def validate_create_table(self, name, username, password):
|
||||
db = MySQLdb.connect(host=self.MASTER_ADDR, user=username, passwd=password, db=name)
|
||||
db = MySQLdb.connect(host=self.MASTER_SERVER, port=3306, user=username, passwd=password, db=name)
|
||||
cur = db.cursor()
|
||||
cur.execute('CREATE TABLE test;')
|
||||
|
||||
def validate_delete(self, name, username, password):
|
||||
self.asseRaises(MySQLdb.ConnectionError,
|
||||
MySQLdb.connect(host=self.MASTER_ADDR, user=username, passwd=password, db=name))
|
||||
self.validate_create_table, name, username, password)
|
||||
|
||||
|
||||
|
||||
class RESTDatabaseTest(DatabaseTestMixin):
|
||||
def add(self, dbname):
|
||||
self.api.databases.create(name=dbname)
|
||||
class RESTDatabaseMixin(DatabaseTestMixin):
|
||||
def setUp(self):
|
||||
super(RESTDatabaseMixin, self).setUp()
|
||||
self.rest_login()
|
||||
|
||||
@save_response_on_error
|
||||
def add(self, dbname, username, password):
|
||||
user = self.rest.databaseusers.create(username=username, password=password)
|
||||
self.rest.databases.create(name=dbname, user=user, type=self.db_type)
|
||||
|
||||
|
||||
class AdminDatabaseMixin(DatabaseTestMixin):
|
||||
def setUp(self):
|
||||
super(AdminDatabaseMixin, self).setUp()
|
||||
self.admin_login()
|
||||
|
||||
@snapshot_on_error
|
||||
def add(self, dbname, username, password):
|
||||
url = self.live_server_url + reverse('admin:databases_database_add')
|
||||
self.selenium.get(url)
|
||||
|
||||
type_input = self.selenium.find_element_by_id('id_type')
|
||||
type_select = Select(type_input)
|
||||
type_select.select_by_value(self.db_type)
|
||||
|
||||
name_field = self.selenium.find_element_by_id('id_name')
|
||||
name_field.send_keys(dbname)
|
||||
|
||||
username_field = self.selenium.find_element_by_id('id_username')
|
||||
username_field.send_keys(username)
|
||||
|
||||
password_field = self.selenium.find_element_by_id('id_password1')
|
||||
password_field.send_keys(password)
|
||||
password_field = self.selenium.find_element_by_id('id_password2')
|
||||
password_field.send_keys(password)
|
||||
|
||||
name_field.submit()
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
|
||||
|
||||
class RESTMysqlDatabaseTest(MySQLBackendMixin, RESTDatabaseMixin, BaseLiveServerTestCase):
|
||||
pass
|
||||
|
||||
|
||||
class AdminMysqlDatabaseTest(MySQLBackendMixin, AdminDatabaseMixin, BaseLiveServerTestCase):
|
||||
pass
|
||||
|
|
|
@ -257,12 +257,7 @@ class AdminDomainMixin(DomainTestMixin):
|
|||
@snapshot_on_error
|
||||
def delete(self, domain_name):
|
||||
domain = Domain.objects.get(name=domain_name)
|
||||
delete = reverse('admin:domains_domain_delete', args=(domain.pk,))
|
||||
url = self.live_server_url + delete
|
||||
self.selenium.get(url)
|
||||
form = self.selenium.find_element_by_name('post')
|
||||
form.submit()
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
self.admin_delete(domain)
|
||||
|
||||
@snapshot_on_error
|
||||
def update(self, domain_name, records):
|
||||
|
|
|
@ -43,7 +43,7 @@ class MailboxAdmin(ChangePasswordAdminMixin, AccountAdminMixin, ExtendedModelAdm
|
|||
fieldsets = (
|
||||
(None, {
|
||||
'classes': ('wide',),
|
||||
'fields': ('account_link', 'name', 'password'),
|
||||
'fields': ('name', 'password', 'is_active', 'account_link'),
|
||||
}),
|
||||
(_("Filtering"), {
|
||||
'classes': ('collapse',),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from rest_framework import viewsets
|
||||
|
||||
from orchestra.api import router
|
||||
from orchestra.api import router, SetPasswordApiMixin
|
||||
from orchestra.apps.accounts.api import AccountApiMixin
|
||||
|
||||
from .models import Address, Mailbox
|
||||
|
@ -13,7 +13,7 @@ class AddressViewSet(AccountApiMixin, viewsets.ModelViewSet):
|
|||
|
||||
|
||||
|
||||
class MailboxViewSet(AccountApiMixin, viewsets.ModelViewSet):
|
||||
class MailboxViewSet(SetPasswordApiMixin, AccountApiMixin, viewsets.ModelViewSet):
|
||||
model = Mailbox
|
||||
serializer_class = MailboxSerializer
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ class PasswdVirtualUserBackend(ServiceController):
|
|||
def set_user(self, context):
|
||||
self.append(textwrap.dedent("""
|
||||
if [[ $( grep "^%(username)s:" %(passwd_path)s ) ]]; then
|
||||
sed -i "s/^%(username)s:.*/%(passwd)s/" %(passwd_path)s
|
||||
sed -i 's#^%(username)s:.*#%(passwd)s#' %(passwd_path)s
|
||||
else
|
||||
echo '%(passwd)s' >> %(passwd_path)s
|
||||
fi""" % context
|
||||
|
@ -49,22 +49,6 @@ class PasswdVirtualUserBackend(ServiceController):
|
|||
context['filter_path'] = os.path.join(context['home'], '.orchestra.sieve')
|
||||
self.append("echo '%(filtering)s' > %(filter_path)s" % context)
|
||||
|
||||
def set_quota(self, mailbox, context):
|
||||
if not hasattr(mailbox, 'resources'):
|
||||
return
|
||||
context.update({
|
||||
'maildir_path': '~%(username)s/Maildir' % context,
|
||||
'maildirsize_path': '~%(username)s/Maildir/maildirsize' % context,
|
||||
'quota': mailbox.resources.disk.allocated*1000*1000,
|
||||
})
|
||||
self.append("mkdir -p %(maildir_path)s" % context)
|
||||
self.append(textwrap.dedent("""
|
||||
sed -i '1s/.*/%(quota)s,S/' %(maildirsize_path)s || {
|
||||
echo '%(quota)s,S' > %(maildirsize_path)s &&
|
||||
chown %(username)s %(maildirsize_path)s;
|
||||
}""" % context
|
||||
))
|
||||
|
||||
def save(self, mailbox):
|
||||
context = self.get_context(mailbox)
|
||||
self.set_user(context)
|
||||
|
@ -73,9 +57,11 @@ class PasswdVirtualUserBackend(ServiceController):
|
|||
def delete(self, mailbox):
|
||||
context = self.get_context(mailbox)
|
||||
self.append("{ sleep 2 && killall -u %(uid)s -s KILL; } &" % context)
|
||||
self.append("killall -u %(uid)s" % context)
|
||||
self.append("killall -u %(uid)s || true" % context)
|
||||
self.append("sed -i '/^%(username)s:.*/d' %(passwd_path)s" % context)
|
||||
self.append("rm -fr %(home)s" % context)
|
||||
# TODO delete
|
||||
context['deleted'] = context['home'].rstrip('/') + '.deleted'
|
||||
self.append("mv %(home)s %(deleted)s" % context)
|
||||
|
||||
def get_extra_fields(self, mailbox, context):
|
||||
context['quota'] = self.get_quota(mailbox)
|
||||
|
|
|
@ -36,7 +36,10 @@ class Mailbox(models.Model):
|
|||
|
||||
@cached_property
|
||||
def active(self):
|
||||
return self.is_active and self.account.is_active
|
||||
try:
|
||||
return self.is_active and self.account.is_active
|
||||
except type(self).account.field.rel.to.DoesNotExist:
|
||||
return self.is_active
|
||||
|
||||
def set_password(self, raw_password):
|
||||
self.password = make_password(raw_password)
|
||||
|
|
|
@ -9,7 +9,7 @@ class MailboxSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeri
|
|||
class Meta:
|
||||
model = Mailbox
|
||||
# TODO 'use_custom_filtering',
|
||||
fields = ('url', 'name', 'password', 'custom_filtering', 'addresses')
|
||||
fields = ('url', 'name', 'password', 'custom_filtering', 'addresses', 'is_active')
|
||||
|
||||
def validate_password(self, attrs, source):
|
||||
""" POST only password """
|
||||
|
@ -44,6 +44,6 @@ class AddressSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeri
|
|||
|
||||
def validate(self, attrs):
|
||||
if not attrs['mailboxes'] and not attrs['forward']:
|
||||
raise serializers.ValidationError("mailboxes or forward should be provided")
|
||||
raise serializers.ValidationError("mailboxes or forward addresses should be provided")
|
||||
return attrs
|
||||
|
||||
|
|
|
@ -20,31 +20,8 @@ from orchestra.utils.tests import BaseLiveServerTestCase, random_ascii, snapshot
|
|||
|
||||
from ... import backends, settings
|
||||
from ...models import Mailbox
|
||||
#>>> mail.list()
|
||||
#('OK', ['(\\HasNoChildren) "." INBOX'])
|
||||
#>>> mail.select('INBOX')
|
||||
#('OK', ['18'])
|
||||
|
||||
#>>> mail.getquota('INBOX')
|
||||
#imaplib.error: GETQUOTA command error: BAD ['Error in IMAP command GETQUOTA: Unknown command.']
|
||||
|
||||
#mail.fetch(10, '(RFC822)')
|
||||
#('OK', [('10 (FLAGS (\\Seen) RFC822 {550}', 'Return-Path: <root@test3.orchestra.lan>\r\nDelivered-To: <rata@orchestra.lan>\r\nReceived: from test3.orchestra.lan\r\n\tby test3.orchestra.lan (Dovecot) with LMTP id hvDUEAIKL1QlOQAAL4hJug\r\n\tfor <rata@orchestra.lan>; Fri, 03 Oct 2014 16:41:38 -0400\r\nReceived: by test3.orchestra.lan (Postfix, from userid 0)\r\n\tid 43BB1F94633; Fri, 3 Oct 2014 16:41:38 -0400 (EDT)\r\nTo: rata@orchestra.lan\r\nSubject: hola\r\nMessage-Id: <20141003204138.43BB1F94633@test3.orchestra.lan>\r\nDate: Fri, 3 Oct 2014 16:41:38 -0400 (EDT)\r\nFrom: root@test3.orchestra.lan (root)\r\n\r\n\r\n\r\n'), ')'])
|
||||
#>>> mail.close()
|
||||
#('OK', ['Close completed.'])
|
||||
|
||||
|
||||
|
||||
#pop = poplib.POP3('localhost')
|
||||
#pop.user('rata')
|
||||
#pop.pass_('3')
|
||||
#>>> pop.list()
|
||||
#('+OK 18 messages:', ['1 552', '2 550', '3 550', '4 548', '5 546', '6 546', '7 554', '8 548', '9 550', '10 550', '11 546', '12 546', '13 546', '14 544', '15 548', '16 577', '17 546', '18 546'], 135)
|
||||
#>>> pop.quit()
|
||||
#'+OK Logging out.'
|
||||
|
||||
|
||||
# FIXME django load production database at the begining of tests
|
||||
class MailboxMixin(object):
|
||||
MASTER_SERVER = os.environ.get('ORCHESTRA_SLAVE_SERVER', 'localhost')
|
||||
DEPENDENCIES = (
|
||||
|
@ -56,7 +33,10 @@ class MailboxMixin(object):
|
|||
def setUp(self):
|
||||
super(MailboxMixin, self).setUp()
|
||||
self.add_route()
|
||||
# apps.get_app_config('resources').reload_relations() doesn't work
|
||||
# TODO fix this
|
||||
from django.apps import apps
|
||||
# clean resource relation from other tests
|
||||
apps.get_app_config('resources').reload_relations()
|
||||
djsettings.DEBUG = True
|
||||
|
||||
def add_route(self):
|
||||
|
@ -96,27 +76,6 @@ class MailboxMixin(object):
|
|||
def add_group(self, username, groupname):
|
||||
raise NotImplementedError
|
||||
|
||||
def validate_user(self, username):
|
||||
idcmd = sshr(self.MASTER_SERVER, "id %s" % username)
|
||||
self.assertEqual(0, idcmd.return_code)
|
||||
user = SystemUser.objects.get(username=username)
|
||||
groups = list(user.groups.values_list('username', flat=True))
|
||||
groups.append(user.username)
|
||||
idgroups = idcmd.stdout.strip().split(' ')[2]
|
||||
idgroups = re.findall(r'\d+\((\w+)\)', idgroups)
|
||||
self.assertEqual(set(groups), set(idgroups))
|
||||
|
||||
def validate_delete(self, username):
|
||||
self.assertRaises(SystemUser.DoesNotExist, SystemUser.objects.get, username=username)
|
||||
self.assertRaises(CommandError,
|
||||
sshrun, self.MASTER_SERVER,'id %s' % username, display=False)
|
||||
self.assertRaises(CommandError,
|
||||
sshrun, self.MASTER_SERVER, 'grep "^%s:" /etc/groups' % username, display=False)
|
||||
self.assertRaises(CommandError,
|
||||
sshrun, self.MASTER_SERVER, 'grep "^%s:" /etc/passwd' % username, display=False)
|
||||
self.assertRaises(CommandError,
|
||||
sshrun, self.MASTER_SERVER, 'grep "^%s:" /etc/shadow' % username, display=False)
|
||||
|
||||
def login_imap(self, username, password):
|
||||
mail = imaplib.IMAP4_SSL(self.MASTER_SERVER)
|
||||
status, msg = mail.login(username, password)
|
||||
|
@ -144,6 +103,9 @@ class MailboxMixin(object):
|
|||
finally:
|
||||
server.quit()
|
||||
|
||||
def validate_mailbox(self, username):
|
||||
sshrun(self.MASTER_SERVER, "doveadm search -u %s ALL" % username, display=False)
|
||||
|
||||
def validate_email(self, username, token):
|
||||
home = Mailbox.objects.get(name=username).get_home()
|
||||
sshrun(self.MASTER_SERVER, "grep '%s' %s/Maildir/new/*" % (token, home), display=False)
|
||||
|
@ -152,14 +114,15 @@ class MailboxMixin(object):
|
|||
username = '%s_mailbox' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password)
|
||||
self.addCleanup(partial(self.delete, username))
|
||||
self.addCleanup(self.delete, username)
|
||||
imap = self.login_imap(username, password)
|
||||
self.validate_mailbox(username)
|
||||
|
||||
def test_change_password(self):
|
||||
username = '%s_systemuser' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password)
|
||||
self.addCleanup(partial(self.delete, username))
|
||||
self.addCleanup(self.delete, username)
|
||||
imap = self.login_imap(username, password)
|
||||
new_password = '@!?%spppP001' % random_ascii(5)
|
||||
self.change_password(username, new_password)
|
||||
|
@ -171,7 +134,7 @@ class MailboxMixin(object):
|
|||
self.add_quota_resource()
|
||||
quota = 100
|
||||
self.add(username, password, quota=quota)
|
||||
self.addCleanup(partial(self.delete, username))
|
||||
self.addCleanup(self.delete, username)
|
||||
get_quota = "doveadm quota get -u %s 2>&1|grep STORAGE|awk {'print $5'}" % username
|
||||
stdout = sshrun(self.MASTER_SERVER, get_quota, display=False).stdout
|
||||
self.assertEqual(quota*1024, int(stdout))
|
||||
|
@ -183,7 +146,7 @@ class MailboxMixin(object):
|
|||
username = '%s_mailbox' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password)
|
||||
self.addCleanup(partial(self.delete, username))
|
||||
self.addCleanup(self.delete, username)
|
||||
msg = MIMEText("Hola bishuns")
|
||||
msg['To'] = 'noexists@example.com'
|
||||
msg['From'] = '%s@%s' % (username, self.MASTER_SERVER)
|
||||
|
@ -199,7 +162,7 @@ class MailboxMixin(object):
|
|||
username = '%s_mailbox' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password)
|
||||
self.addCleanup(partial(self.delete, username))
|
||||
self.addCleanup(self.delete, username)
|
||||
domain = '%s_domain.lan' % random_ascii(5)
|
||||
name = '%s_name' % random_ascii(5)
|
||||
domain = self.account.domains.create(name=domain)
|
||||
|
@ -207,6 +170,47 @@ class MailboxMixin(object):
|
|||
token = random_ascii(100)
|
||||
self.send_email("%s@%s" % (name, domain), token)
|
||||
self.validate_email(username, token)
|
||||
|
||||
def test_disable(self):
|
||||
username = '%s_systemuser' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password)
|
||||
self.validate_mailbox(username)
|
||||
self.addCleanup(self.delete, username)
|
||||
imap = self.login_imap(username, password)
|
||||
self.disable(username)
|
||||
self.assertRaises(imap.error, self.login_imap, username, password)
|
||||
|
||||
def test_delete(self):
|
||||
username = '%s_systemuser' % random_ascii(10)
|
||||
password = '@!?%sppppP001' % random_ascii(5)
|
||||
self.add(username, password)
|
||||
imap = self.login_imap(username, password)
|
||||
self.validate_mailbox(username)
|
||||
mailbox = Mailbox.objects.get(name=username)
|
||||
home = mailbox.get_home()
|
||||
self.delete(username)
|
||||
self.assertRaises(Mailbox.DoesNotExist, Mailbox.objects.get, name=username)
|
||||
self.assertRaises(CommandError, self.validate_mailbox, username)
|
||||
self.assertRaises(imap.error, self.login_imap, username, password)
|
||||
self.assertRaises(CommandError,
|
||||
sshrun, self.MASTER_SERVER, 'ls %s' % home, display=False)
|
||||
|
||||
def test_delete_address(self):
|
||||
username = '%s_mailbox' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password)
|
||||
self.addCleanup(self.delete, username)
|
||||
domain = '%s_domain.lan' % random_ascii(5)
|
||||
name = '%s_name' % random_ascii(5)
|
||||
domain = self.account.domains.create(name=domain)
|
||||
self.add_address(username, name, domain)
|
||||
token = random_ascii(100)
|
||||
self.send_email("%s@%s" % (name, domain), token)
|
||||
self.validate_email(username, token)
|
||||
self.delete_address(username)
|
||||
self.send_email("%s@%s" % (name, domain), token)
|
||||
self.validate_email(username, token)
|
||||
|
||||
|
||||
class RESTMailboxMixin(MailboxMixin):
|
||||
|
@ -243,7 +247,21 @@ class RESTMailboxMixin(MailboxMixin):
|
|||
mailbox = self.rest.mailboxes.retrieve(name=username).get()
|
||||
domain = self.rest.domains.retrieve(name=domain.name).get()
|
||||
self.rest.addresses.create(name=name, domain=domain, mailboxes=[mailbox])
|
||||
|
||||
|
||||
@save_response_on_error
|
||||
def delete_address(self, username):
|
||||
mailbox = self.rest.mailboxes.retrieve(name=username).get()
|
||||
self.rest.addresses.delete()
|
||||
|
||||
@save_response_on_error
|
||||
def change_password(self, username, password):
|
||||
mailbox = self.rest.mailboxes.retrieve(name=username).get()
|
||||
mailbox.set_password(password=password)
|
||||
|
||||
@save_response_on_error
|
||||
def disable(self, username):
|
||||
mailbox = self.rest.mailboxes.retrieve(name=username).get()
|
||||
mailbox.update(is_active=False)
|
||||
|
||||
|
||||
class AdminMailboxMixin(MailboxMixin):
|
||||
|
@ -267,8 +285,12 @@ class AdminMailboxMixin(MailboxMixin):
|
|||
password_field.send_keys(password)
|
||||
password_field = self.selenium.find_element_by_id('id_password2')
|
||||
password_field.send_keys(password)
|
||||
|
||||
if quota is not None:
|
||||
from orchestra.admin.utils import get_modeladmin
|
||||
m = get_modeladmin(Mailbox)
|
||||
print 't', type(m).inlines
|
||||
print 'm', m.inlines
|
||||
self.take_screenshot()
|
||||
quota_field = self.selenium.find_element_by_id(
|
||||
'id_resources-resourcedata-content_type-object_id-0-allocated')
|
||||
quota_field.clear()
|
||||
|
@ -280,27 +302,12 @@ class AdminMailboxMixin(MailboxMixin):
|
|||
@snapshot_on_error
|
||||
def delete(self, username):
|
||||
mailbox = Mailbox.objects.get(name=username)
|
||||
delete = reverse('admin:mails_mailbox_delete', args=(mailbox.pk,))
|
||||
url = self.live_server_url + delete
|
||||
self.selenium.get(url)
|
||||
confirmation = self.selenium.find_element_by_name('post')
|
||||
confirmation.submit()
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
self.admin_delete(mailbox)
|
||||
|
||||
@snapshot_on_error
|
||||
def change_password(self, username, password):
|
||||
mailbox = Mailbox.objects.get(name=username)
|
||||
change_password = reverse('admin:mails_mailbox_change_password', args=(mailbox.pk,))
|
||||
url = self.live_server_url + change_password
|
||||
self.selenium.get(url)
|
||||
|
||||
password_field = self.selenium.find_element_by_id('id_password1')
|
||||
password_field.send_keys(password)
|
||||
password_field = self.selenium.find_element_by_id('id_password2')
|
||||
password_field.send_keys(password)
|
||||
password_field.submit()
|
||||
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
self.admin_change_password(mailbox, password)
|
||||
|
||||
@snapshot_on_error
|
||||
def add_address(self, username, name, domain):
|
||||
|
@ -320,6 +327,18 @@ class AdminMailboxMixin(MailboxMixin):
|
|||
name_field.submit()
|
||||
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
|
||||
@snapshot_on_error
|
||||
def delete_address(self, username):
|
||||
mailbox = Mailbox.objects.get(name=username)
|
||||
address = mailbox.addresses.get()
|
||||
self.admin_delete(address)
|
||||
|
||||
@snapshot_on_error
|
||||
def disable(self, username):
|
||||
mailbox = Mailbox.objects.get(name=username)
|
||||
self.admin_disable(mailbox)
|
||||
|
||||
|
||||
class RESTMailboxTest(RESTMailboxMixin, BaseLiveServerTestCase):
|
||||
pass
|
||||
|
|
|
@ -3,11 +3,12 @@ import socket
|
|||
from django.contrib.contenttypes import generic
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
from django.utils.module_loading import autodiscover_modules
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.core.validators import validate_ip_address, ValidationError
|
||||
from orchestra.models.fields import NullableCharField
|
||||
from orchestra.utils.apps import autodiscover
|
||||
#from orchestra.utils.apps import autodiscover
|
||||
|
||||
from . import settings, manager
|
||||
from .backends import ServiceBackend
|
||||
|
@ -133,7 +134,7 @@ class BackendOperation(models.Model):
|
|||
return ServiceBackend.get_backend(self.backend)
|
||||
|
||||
|
||||
autodiscover('backends')
|
||||
autodiscover_modules('backends')
|
||||
|
||||
|
||||
class Route(models.Model):
|
||||
|
|
|
@ -132,23 +132,26 @@ def resource_inline_factory(resources):
|
|||
def has_add_permission(self, *args, **kwargs):
|
||||
""" Hidde add another """
|
||||
return False
|
||||
|
||||
return ResourceInline
|
||||
|
||||
|
||||
from orchestra.utils import database_ready
|
||||
def insert_resource_inlines():
|
||||
# Clean previous state
|
||||
for related in Resource._related:
|
||||
modeladmin = get_modeladmin(related)
|
||||
modeladmin_class = type(modeladmin)
|
||||
for inline in getattr(modeladmin_class, 'inlines', []):
|
||||
if inline.__name__ == 'ResourceInline':
|
||||
modeladmin_class.inlines.remove(inline)
|
||||
modeladmin.inlines = modeladmin_class.inlines
|
||||
|
||||
for ct, resources in Resource.objects.group_by('content_type').iteritems():
|
||||
inline = resource_inline_factory(resources)
|
||||
model = ct.model_class()
|
||||
modeladmin = get_modeladmin(model)
|
||||
inserted = False
|
||||
inlines = []
|
||||
for existing in getattr(modeladmin, 'inlines', []):
|
||||
if type(inline) == type(existing):
|
||||
existing = inline
|
||||
inserted = True
|
||||
inlines.append(existing)
|
||||
if inserted:
|
||||
modeladmin.inlines = inlines
|
||||
else:
|
||||
insertattr(model, 'inlines', inline)
|
||||
insertattr(model, 'inlines', inline)
|
||||
modeladmin.inlines = type(modeladmin).inlines
|
||||
|
||||
if database_ready():
|
||||
insert_resource_inlines()
|
||||
|
|
|
@ -9,15 +9,13 @@ class ResourcesConfig(AppConfig):
|
|||
|
||||
def ready(self):
|
||||
if database_ready():
|
||||
from .admin import insert_resource_inlines
|
||||
from .models import create_resource_relation
|
||||
create_resource_relation()
|
||||
insert_resource_inlines()
|
||||
|
||||
def reload_relations(self):
|
||||
from .admin import insert_resource_inlines
|
||||
from .models import create_resource_relation
|
||||
from .serializers import insert_resource_serializers
|
||||
create_resource_relation()
|
||||
insert_resource_inlines()
|
||||
insert_resource_serializers()
|
||||
create_resource_relation()
|
||||
|
|
|
@ -31,6 +31,7 @@ class Resource(models.Model):
|
|||
(MONTHLY_SUM, _("Monthly Sum")),
|
||||
(MONTHLY_AVG, _("Monthly Average")),
|
||||
)
|
||||
_related = set() # keeps track of related models for resource cleanup
|
||||
|
||||
name = models.CharField(_("name"), max_length=32,
|
||||
help_text=_('Required. 32 characters or fewer. Lowercase letters, '
|
||||
|
@ -102,6 +103,7 @@ class Resource(models.Model):
|
|||
task.save(update_fields=['crontab'])
|
||||
if created:
|
||||
# This only work on tests because of multiprocessing used on real deployments
|
||||
print 'saved'
|
||||
apps.get_app_config('resources').reload_relations()
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
|
@ -192,8 +194,21 @@ def create_resource_relation():
|
|||
self.obj = obj
|
||||
return self
|
||||
|
||||
# Clean previous state
|
||||
for related in Resource._related:
|
||||
try:
|
||||
delattr(related, 'resource_set')
|
||||
delattr(related, 'resources')
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
related._meta.virtual_fields = [
|
||||
field for field in related._meta.virtual_fields if field.rel.to != ResourceData
|
||||
]
|
||||
|
||||
relation = GenericRelation('resources.ResourceData')
|
||||
for ct, resources in Resource.objects.group_by('content_type').iteritems():
|
||||
model = ct.model_class()
|
||||
model.add_to_class('resource_set', relation)
|
||||
model.resources = ResourceHandler()
|
||||
Resource._related.add(model)
|
||||
|
|
|
@ -30,6 +30,15 @@ class ResourceSerializer(serializers.ModelSerializer):
|
|||
# Monkey-patching section
|
||||
|
||||
def insert_resource_serializers():
|
||||
# clean previous state
|
||||
for related in Resource._related:
|
||||
viewset = router.get_viewset(related)
|
||||
fields = list(viewset.serializer_class.Meta.fields)
|
||||
try:
|
||||
fields.remove('resources')
|
||||
except ValueError:
|
||||
pass
|
||||
viewset.serializer_class.Meta.fields = fields
|
||||
# Create nested serializers on target models
|
||||
for ct, resources in Resource.objects.group_by('content_type').iteritems():
|
||||
model = ct.model_class()
|
||||
|
|
|
@ -6,11 +6,12 @@ from django.db.models.loading import get_model
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.validators import ValidationError
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.module_loading import autodiscover_modules
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.core import caches, services, accounts
|
||||
from orchestra.models import queryset
|
||||
from orchestra.utils.apps import autodiscover
|
||||
#from orchestra.utils.apps import autodiscover
|
||||
|
||||
from . import settings, rating
|
||||
from .handlers import ServiceHandler
|
||||
|
@ -70,7 +71,7 @@ class Rate(models.Model):
|
|||
return "{}-{}".format(str(self.price), self.quantity)
|
||||
|
||||
|
||||
autodiscover('handlers')
|
||||
autodiscover_modules('handlers')
|
||||
|
||||
|
||||
class Service(models.Model):
|
||||
|
|
|
@ -37,7 +37,7 @@ class SystemUserBackend(ServiceController):
|
|||
self.append("groupdel %(username)s || true" % context)
|
||||
if user.is_main:
|
||||
# TODO delete instead of this shit
|
||||
context['deleted'] = context['home'][:-1]+'.deleted'
|
||||
context['deleted'] = context['home'].rstrip('/') + '.deleted'
|
||||
self.append("mv %(home)s %(deleted)s" % context)
|
||||
|
||||
def get_groups(self, user):
|
||||
|
|
|
@ -48,7 +48,6 @@ class SystemUser(models.Model):
|
|||
|
||||
@cached_property
|
||||
def active(self):
|
||||
a = type(self).account.field.model
|
||||
try:
|
||||
return self.is_active and self.account.is_active
|
||||
except type(self).account.field.rel.to.DoesNotExist:
|
||||
|
|
|
@ -9,11 +9,20 @@ from orchestra.core.validators import validate_password
|
|||
from .models import SystemUser
|
||||
|
||||
|
||||
class GroupSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = SystemUser
|
||||
fields = ('username',)
|
||||
|
||||
def from_native(self, data, files=None):
|
||||
return SystemUser.objects.get(username=data['username'])
|
||||
|
||||
|
||||
class SystemUserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
|
||||
password = serializers.CharField(max_length=128, label=_('Password'),
|
||||
validators=[validate_password], write_only=True, required=False,
|
||||
widget=widgets.PasswordInput)
|
||||
groups = serializers.RelatedField(many=True)
|
||||
groups = GroupSerializer(many=True, allow_add_remove=True, required=False)
|
||||
|
||||
class Meta:
|
||||
model = SystemUser
|
||||
|
@ -30,6 +39,8 @@ class SystemUserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelS
|
|||
raise serializers.ValidationError(_("Password required"))
|
||||
return attrs
|
||||
|
||||
# TODO validate gruops != self
|
||||
|
||||
def save_object(self, obj, **kwargs):
|
||||
# FIXME this method will be called when saving nested serializers :(
|
||||
if not obj.pk:
|
||||
|
|
|
@ -13,7 +13,8 @@ from selenium.webdriver.support.select import Select
|
|||
from orchestra.apps.accounts.models import Account
|
||||
from orchestra.apps.orchestration.models import Server, Route
|
||||
from orchestra.utils.system import run, sshrun
|
||||
from orchestra.utils.tests import BaseLiveServerTestCase, random_ascii, snapshot_on_error
|
||||
from orchestra.utils.tests import (BaseLiveServerTestCase, random_ascii, snapshot_on_error,
|
||||
save_response_on_error)
|
||||
|
||||
from ... import backends, settings
|
||||
from ...models import SystemUser
|
||||
|
@ -71,13 +72,14 @@ class SystemUserMixin(object):
|
|||
def validate_delete(self, username):
|
||||
self.assertRaises(SystemUser.DoesNotExist, SystemUser.objects.get, username=username)
|
||||
self.assertRaises(CommandError,
|
||||
sshrun, self.MASTER_SERVER,'id %s' % username, display=False)
|
||||
sshrun, self.MASTER_SERVER, 'id %s' % username, display=False)
|
||||
self.assertRaises(CommandError,
|
||||
sshrun, self.MASTER_SERVER, 'grep "^%s:" /etc/groups' % username, display=False)
|
||||
self.assertRaises(CommandError,
|
||||
sshrun, self.MASTER_SERVER, 'grep "^%s:" /etc/passwd' % username, display=False)
|
||||
self.assertRaises(CommandError,
|
||||
sshrun, self.MASTER_SERVER, 'grep "^%s:" /etc/shadow' % username, display=False)
|
||||
# Home will be deleted on account delete, see test_delete_account
|
||||
|
||||
def validate_ftp(self, username, password):
|
||||
connection = ftplib.FTP(self.MASTER_SERVER)
|
||||
|
@ -105,22 +107,24 @@ class SystemUserMixin(object):
|
|||
username = '%s_systemuser' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password)
|
||||
self.addCleanup(partial(self.delete, username))
|
||||
self.addCleanup(self.delete, username)
|
||||
self.validate_user(username)
|
||||
|
||||
def test_ftp(self):
|
||||
username = '%s_systemuser' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password, shell='/dev/null')
|
||||
self.addCleanup(partial(self.delete, username))
|
||||
self.assertRaises(paramiko.AuthenticationException, self.validate_sftp, username, password)
|
||||
self.assertRaises(paramiko.AuthenticationException, self.validate_ssh, username, password)
|
||||
self.addCleanup(self.delete, username)
|
||||
self.assertRaises(paramiko.AuthenticationException,
|
||||
self.validate_sftp, username, password)
|
||||
self.assertRaises(paramiko.AuthenticationException,
|
||||
self.validate_ssh, username, password)
|
||||
|
||||
def test_sftp(self):
|
||||
username = '%s_systemuser' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password, shell='/bin/rssh')
|
||||
self.addCleanup(partial(self.delete, username))
|
||||
self.addCleanup(self.delete, username)
|
||||
self.validate_sftp(username, password)
|
||||
self.assertRaises(AssertionError, self.validate_ssh, username, password)
|
||||
|
||||
|
@ -128,7 +132,7 @@ class SystemUserMixin(object):
|
|||
username = '%s_systemuser' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password, shell='/bin/bash')
|
||||
self.addCleanup(partial(self.delete, username))
|
||||
self.addCleanup(self.delete, username)
|
||||
self.validate_ssh(username, password)
|
||||
|
||||
def test_delete(self):
|
||||
|
@ -143,12 +147,12 @@ class SystemUserMixin(object):
|
|||
username = '%s_systemuser' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password)
|
||||
self.addCleanup(partial(self.delete, username))
|
||||
self.addCleanup(self.delete, username)
|
||||
self.validate_user(username)
|
||||
username2 = '%s_systemuser' % random_ascii(10)
|
||||
password2 = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username2, password2)
|
||||
self.addCleanup(partial(self.delete, username2))
|
||||
self.addCleanup(self.delete, username2)
|
||||
self.validate_user(username2)
|
||||
self.add_group(username, username2)
|
||||
user = SystemUser.objects.get(username=username)
|
||||
|
@ -160,7 +164,7 @@ class SystemUserMixin(object):
|
|||
username = '%s_systemuser' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password, shell='/dev/null')
|
||||
self.addCleanup(partial(self.delete, username))
|
||||
self.addCleanup(self.delete, username)
|
||||
self.validate_ftp(username, password)
|
||||
self.disable(username)
|
||||
self.validate_user(username)
|
||||
|
@ -170,7 +174,7 @@ class SystemUserMixin(object):
|
|||
username = '%s_systemuser' % random_ascii(10)
|
||||
password = '@!?%spppP001' % random_ascii(5)
|
||||
self.add(username, password)
|
||||
self.addCleanup(partial(self.delete, username))
|
||||
self.addCleanup(self.delete, username)
|
||||
self.validate_ftp(username, password)
|
||||
new_password = '@!?%spppP001' % random_ascii(5)
|
||||
self.change_password(username, new_password)
|
||||
|
@ -185,33 +189,38 @@ class RESTSystemUserMixin(SystemUserMixin):
|
|||
self.rest_login()
|
||||
# create main user
|
||||
self.save(self.account.username)
|
||||
self.addCleanup(partial(self.delete, self.account.username))
|
||||
self.addCleanup(self.delete, self.account.username)
|
||||
|
||||
@save_response_on_error
|
||||
def add(self, username, password, shell='/dev/null'):
|
||||
self.rest.systemusers.create(username=username, password=password, shell=shell)
|
||||
|
||||
@save_response_on_error
|
||||
def delete(self, username):
|
||||
user = self.rest.systemusers.retrieve(username=username).get()
|
||||
user.delete()
|
||||
|
||||
@save_response_on_error
|
||||
def add_group(self, username, groupname):
|
||||
user = self.rest.systemusers.retrieve(username=username).get()
|
||||
group = self.rest.systemusers.retrieve(username=groupname).get()
|
||||
user.groups.append(group) # TODO
|
||||
user.groups.append({'username': groupname})
|
||||
user.save()
|
||||
|
||||
@save_response_on_error
|
||||
def disable(self, username):
|
||||
user = self.rest.systemusers.retrieve(username=username).get()
|
||||
user.is_active = False
|
||||
user.save()
|
||||
|
||||
@save_response_on_error
|
||||
def save(self, username):
|
||||
user = self.rest.systemusers.retrieve(username=username).get()
|
||||
user.save()
|
||||
|
||||
@save_response_on_error
|
||||
def change_password(self, username, password):
|
||||
user = self.rest.systemusers.retrieve(username=username).get()
|
||||
user.change_password(password)
|
||||
user.set_password(password)
|
||||
|
||||
|
||||
class AdminSystemUserMixin(SystemUserMixin):
|
||||
|
@ -220,7 +229,7 @@ class AdminSystemUserMixin(SystemUserMixin):
|
|||
self.admin_login()
|
||||
# create main user
|
||||
self.save(self.account.username)
|
||||
self.addCleanup(partial(self.delete, self.account.username))
|
||||
self.addCleanup(self.delete, self.account.username)
|
||||
|
||||
@snapshot_on_error
|
||||
def add(self, username, password, shell='/dev/null'):
|
||||
|
@ -249,24 +258,12 @@ class AdminSystemUserMixin(SystemUserMixin):
|
|||
@snapshot_on_error
|
||||
def delete(self, username):
|
||||
user = SystemUser.objects.get(username=username)
|
||||
delete = reverse('admin:systemusers_systemuser_delete', args=(user.pk,))
|
||||
url = self.live_server_url + delete
|
||||
self.selenium.get(url)
|
||||
confirmation = self.selenium.find_element_by_name('post')
|
||||
confirmation.submit()
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
self.admin_delete(user)
|
||||
|
||||
@snapshot_on_error
|
||||
def disable(self, username):
|
||||
user = SystemUser.objects.get(username=username)
|
||||
change = reverse('admin:systemusers_systemuser_change', args=(user.pk,))
|
||||
url = self.live_server_url + change
|
||||
self.selenium.get(url)
|
||||
is_active = self.selenium.find_element_by_id('id_is_active')
|
||||
is_active.click()
|
||||
save = self.selenium.find_element_by_name('_save')
|
||||
save.submit()
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
self.admin_disable(user)
|
||||
|
||||
@snapshot_on_error
|
||||
def add_group(self, username, groupname):
|
||||
|
@ -294,17 +291,8 @@ class AdminSystemUserMixin(SystemUserMixin):
|
|||
@snapshot_on_error
|
||||
def change_password(self, username, password):
|
||||
user = SystemUser.objects.get(username=username)
|
||||
change_password = reverse('admin:systemusers_systemuser_change_password', args=(user.pk,))
|
||||
url = self.live_server_url + change_password
|
||||
self.selenium.get(url)
|
||||
|
||||
password_field = self.selenium.find_element_by_id('id_password1')
|
||||
password_field.send_keys(password)
|
||||
password_field = self.selenium.find_element_by_id('id_password2')
|
||||
password_field.send_keys(password)
|
||||
password_field.submit()
|
||||
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
self.admin_change_password(user, password)
|
||||
|
||||
|
||||
class RESTSystemUserTest(RESTSystemUserMixin, BaseLiveServerTestCase):
|
||||
pass
|
||||
|
@ -337,10 +325,10 @@ class AdminSystemUserTest(AdminSystemUserMixin, BaseLiveServerTestCase):
|
|||
email = self.selenium.find_element_by_id('id_contacts-0-email')
|
||||
email.send_keys(account_email)
|
||||
email.submit()
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
|
||||
account = Account.objects.get(username=account_username)
|
||||
self.addCleanup(account.delete)
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
self.addCleanup(self.delete, account_username)
|
||||
self.assertEqual(0, sshr(self.MASTER_SERVER, "id %s" % account.username).return_code)
|
||||
|
||||
@snapshot_on_error
|
||||
|
|
|
@ -130,7 +130,8 @@ function install_requirements () {
|
|||
libxml2-dev \
|
||||
libxslt1-dev \
|
||||
wkhtmltopdf \
|
||||
xvfb"
|
||||
xvfb \
|
||||
python-mysqldb"
|
||||
|
||||
PIP="django==1.7 \
|
||||
django-celery-email==1.0.4 \
|
||||
|
|
|
@ -107,7 +107,7 @@ INSTALLED_APPS = (
|
|||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.admin.apps.SimpleAdminConfig',
|
||||
|
||||
'orchestra.apps.accounts',
|
||||
'orchestra.apps.contacts',
|
||||
|
|
|
@ -25,9 +25,9 @@ urlpatterns = patterns('',
|
|||
url(r'^media/(?P<path>.*)$', 'django.views.static.serve',
|
||||
{'document_root': settings.MEDIA_ROOT, 'show_indexes': True}
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
|
||||
if settings.DEBUG:
|
||||
import debug_toolbar
|
||||
urlpatterns += patterns('',
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
from django.utils.importlib import import_module
|
||||
from django.utils.module_loading import module_has_submodule
|
||||
#from django.utils.importlib import import_module
|
||||
#from django.utils.module_loading import module_has_submodule
|
||||
|
||||
def autodiscover(module):
|
||||
""" Auto-discover INSTALLED_APPS module.py """
|
||||
from django.conf import settings
|
||||
for app in settings.INSTALLED_APPS:
|
||||
mod = import_module(app)
|
||||
try:
|
||||
import_module('%s.%s' % (app, module))
|
||||
except ImportError:
|
||||
# Decide whether to bubble up this error. If the app just
|
||||
# doesn't have the module, we can ignore the error
|
||||
# attempting to import it, otherwise we want it to bubble up.
|
||||
if module_has_submodule(mod, module):
|
||||
print '%s module caused this error:' % module
|
||||
raise
|
||||
|
||||
#def autodiscover(module):
|
||||
# """ Auto-discover INSTALLED_APPS module.py """
|
||||
# from django.conf import settings
|
||||
# for app in settings.INSTALLED_APPS:
|
||||
# mod = import_module(app)
|
||||
# try:
|
||||
# import_module('%s.%s' % (app, module))
|
||||
# except ImportError:
|
||||
# # Decide whether to bubble up this error. If the app just
|
||||
# # doesn't have the module, we can ignore the error
|
||||
# # attempting to import it, otherwise we want it to bubble up.
|
||||
# if module_has_submodule(mod, module):
|
||||
# print '%s module caused this error:' % module
|
||||
# raise
|
||||
|
||||
def isinstalled(app):
|
||||
""" returns True if app is installed """
|
||||
|
|
|
@ -7,6 +7,7 @@ from functools import wraps
|
|||
from django.conf import settings
|
||||
from django.contrib.auth import BACKEND_SESSION_KEY, SESSION_KEY, get_user_model
|
||||
from django.contrib.sessions.backends.db import SessionStore
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import LiveServerTestCase, TestCase
|
||||
from orm.api import Api
|
||||
from selenium.webdriver.firefox.webdriver import WebDriver
|
||||
|
@ -115,7 +116,43 @@ class BaseLiveServerTestCase(AppDependencyMixin, LiveServerTestCase):
|
|||
filename = 'screenshot_%s_%s.png' % (self.id(), timestamp)
|
||||
path = '/home/orchestra/snapshots'
|
||||
self.selenium.save_screenshot(os.path.join(path, filename))
|
||||
|
||||
|
||||
def admin_delete(self, obj):
|
||||
opts = obj._meta
|
||||
app_label, model_name = opts.app_label, opts.model_name
|
||||
delete = reverse('admin:%s_%s_delete' % (app_label, model_name), args=(obj.pk,))
|
||||
url = self.live_server_url + delete
|
||||
self.selenium.get(url)
|
||||
confirmation = self.selenium.find_element_by_name('post')
|
||||
confirmation.submit()
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
|
||||
def admin_disable(self, obj):
|
||||
opts = obj._meta
|
||||
app_label, model_name = opts.app_label, opts.model_name
|
||||
change = reverse('admin:%s_%s_change' % (app_label, model_name), args=(obj.pk,))
|
||||
url = self.live_server_url + change
|
||||
self.selenium.get(url)
|
||||
is_active = self.selenium.find_element_by_id('id_is_active')
|
||||
is_active.click()
|
||||
save = self.selenium.find_element_by_name('_save')
|
||||
save.submit()
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
|
||||
def admin_change_password(self, obj, password):
|
||||
opts = obj._meta
|
||||
app_label, model_name = opts.app_label, opts.model_name
|
||||
change_password = reverse('admin:%s_%s_change_password' % (app_label, model_name), args=(obj.pk,))
|
||||
url = self.live_server_url + change_password
|
||||
self.selenium.get(url)
|
||||
|
||||
password_field = self.selenium.find_element_by_id('id_password1')
|
||||
password_field.send_keys(password)
|
||||
password_field = self.selenium.find_element_by_id('id_password2')
|
||||
password_field.send_keys(password)
|
||||
password_field.submit()
|
||||
|
||||
self.assertNotEqual(url, self.selenium.current_url)
|
||||
|
||||
def snapshot_on_error(test):
|
||||
@wraps(test)
|
||||
|
|
|
@ -2,3 +2,5 @@ MySQL
|
|||
=====
|
||||
|
||||
apt-get install mysql-server
|
||||
|
||||
sed -i "s/bind-address = 127.0.0.1/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
|
||||
|
|
Loading…
Reference in a new issue