diff --git a/apps/common/__init__.py b/apps/common/__init__.py index 77519a42d3..7c25469ed5 100644 --- a/apps/common/__init__.py +++ b/apps/common/__init__.py @@ -4,9 +4,10 @@ import tempfile from django.utils.translation import ugettext_lazy as _ from django.contrib.auth import models as auth_models +from django.contrib.auth.models import User from django.contrib.auth.management import create_superuser from django.dispatch import receiver -from django.db.models.signals import post_syncdb +from django.db.models.signals import post_syncdb, post_save from navigation.api import register_links, register_top_menu @@ -14,6 +15,7 @@ from .conf.settings import (AUTO_CREATE_ADMIN, AUTO_ADMIN_USERNAME, AUTO_ADMIN_PASSWORD, TEMPORARY_DIRECTORY) from .conf import settings as common_settings from .utils import validate_path +from .models import AutoAdminSingleton def has_usable_password(context): @@ -53,9 +55,22 @@ def create_superuser(sender, **kwargs): print '*' * 80 print 'Creating super admin user -- login: %s, password: %s' % (AUTO_ADMIN_USERNAME, AUTO_ADMIN_PASSWORD) print '*' * 80 - assert auth_models.User.objects.create_superuser(AUTO_ADMIN_USERNAME, 'x@x.com', AUTO_ADMIN_PASSWORD) + assert auth_models.User.objects.create_superuser(AUTO_ADMIN_USERNAME, 'autoadmin@autoadmin.com', AUTO_ADMIN_PASSWORD) + admin = auth_models.User.objects.get(username=AUTO_ADMIN_USERNAME) + auto_admin_properties = AutoAdminSingleton.objects.get() + auto_admin_properties.auto_admin_account = admin + auto_admin_properties.auto_admin_password = AUTO_ADMIN_PASSWORD + auto_admin_properties.save() else: print 'Super admin user already exists. -- login: %s' % AUTO_ADMIN_USERNAME + +@receiver(post_save, dispatch_uid='auto_admin_account_passwd_change', sender=User) +def auto_admin_account_passwd_change(sender, instance, **kwargs): + auto_admin_properties = AutoAdminSingleton.objects.get() + if instance == auto_admin_properties.auto_admin_account: + auto_admin_properties.delete(force=True) + + if (validate_path(TEMPORARY_DIRECTORY) == False) or (not TEMPORARY_DIRECTORY): setattr(common_settings, 'TEMPORARY_DIRECTORY', tempfile.mkdtemp()) diff --git a/apps/common/conf/settings.py b/apps/common/conf/settings.py index e1ec67ada3..83d71ab889 100644 --- a/apps/common/conf/settings.py +++ b/apps/common/conf/settings.py @@ -1,6 +1,7 @@ """Configuration options for the common app""" from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.models import User from smart_settings.api import register_setting @@ -53,7 +54,7 @@ register_setting( module=u'common.conf.settings', name=u'AUTO_ADMIN_PASSWORD', global_name=u'COMMON_AUTO_ADMIN_PASSWORD', - default=u'admin', + default=User.objects.make_random_password(), ) register_setting( diff --git a/apps/common/migrations/0001_initial.py b/apps/common/migrations/0001_initial.py new file mode 100644 index 0000000000..eb6074f1a1 --- /dev/null +++ b/apps/common/migrations/0001_initial.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'AnonymousUserSingleton' + db.create_table('common_anonymoususersingleton', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('lock_id', self.gf('django.db.models.fields.CharField')(default=1, unique=True, max_length=1)), + )) + db.send_create_signal('common', ['AnonymousUserSingleton']) + + + def backwards(self, orm): + # Deleting model 'AnonymousUserSingleton' + db.delete_table('common_anonymoususersingleton') + + + models = { + 'common.anonymoususersingleton': { + 'Meta': {'object_name': 'AnonymousUserSingleton'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lock_id': ('django.db.models.fields.CharField', [], {'default': '1', 'unique': 'True', 'max_length': '1'}) + } + } + + complete_apps = ['common'] \ No newline at end of file diff --git a/apps/common/migrations/0002_auto__add_autoadminsingleton.py b/apps/common/migrations/0002_auto__add_autoadminsingleton.py new file mode 100644 index 0000000000..5ab3627d3f --- /dev/null +++ b/apps/common/migrations/0002_auto__add_autoadminsingleton.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'AutoAdminSingleton' + db.create_table('common_autoadminsingleton', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('lock_id', self.gf('django.db.models.fields.CharField')(default=1, unique=True, max_length=1)), + ('original_auto_admin_password', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('auto_admin', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)), + )) + db.send_create_signal('common', ['AutoAdminSingleton']) + + + def backwards(self, orm): + # Deleting model 'AutoAdminSingleton' + db.delete_table('common_autoadminsingleton') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'common.anonymoususersingleton': { + 'Meta': {'object_name': 'AnonymousUserSingleton'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lock_id': ('django.db.models.fields.CharField', [], {'default': '1', 'unique': 'True', 'max_length': '1'}) + }, + 'common.autoadminsingleton': { + 'Meta': {'object_name': 'AutoAdminSingleton'}, + 'auto_admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lock_id': ('django.db.models.fields.CharField', [], {'default': '1', 'unique': 'True', 'max_length': '1'}), + 'original_auto_admin_password': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['common'] \ No newline at end of file diff --git a/apps/common/migrations/0003_auto__del_field_autoadminsingleton_original_auto_admin_password__del_f.py b/apps/common/migrations/0003_auto__del_field_autoadminsingleton_original_auto_admin_password__del_f.py new file mode 100644 index 0000000000..e02fceb6e6 --- /dev/null +++ b/apps/common/migrations/0003_auto__del_field_autoadminsingleton_original_auto_admin_password__del_f.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Deleting field 'AutoAdminSingleton.original_auto_admin_password' + db.delete_column('common_autoadminsingleton', 'original_auto_admin_password') + + # Deleting field 'AutoAdminSingleton.auto_admin' + db.delete_column('common_autoadminsingleton', 'auto_admin_id') + + # Adding field 'AutoAdminSingleton.auto_admin_account' + db.add_column('common_autoadminsingleton', 'auto_admin_account', + self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='auto_admin_account', null=True, to=orm['auth.User']), + keep_default=False) + + # Adding field 'AutoAdminSingleton.auto_admin_password' + db.add_column('common_autoadminsingleton', 'auto_admin_password', + self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Adding field 'AutoAdminSingleton.original_auto_admin_password' + db.add_column('common_autoadminsingleton', 'original_auto_admin_password', + self.gf('django.db.models.fields.BooleanField')(default=True), + keep_default=False) + + # Adding field 'AutoAdminSingleton.auto_admin' + db.add_column('common_autoadminsingleton', 'auto_admin', + self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True), + keep_default=False) + + # Deleting field 'AutoAdminSingleton.auto_admin_account' + db.delete_column('common_autoadminsingleton', 'auto_admin_account_id') + + # Deleting field 'AutoAdminSingleton.auto_admin_password' + db.delete_column('common_autoadminsingleton', 'auto_admin_password') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'common.anonymoususersingleton': { + 'Meta': {'object_name': 'AnonymousUserSingleton'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lock_id': ('django.db.models.fields.CharField', [], {'default': '1', 'unique': 'True', 'max_length': '1'}) + }, + 'common.autoadminsingleton': { + 'Meta': {'object_name': 'AutoAdminSingleton'}, + 'auto_admin_account': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'auto_admin_account'", 'null': 'True', 'to': "orm['auth.User']"}), + 'auto_admin_password': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lock_id': ('django.db.models.fields.CharField', [], {'default': '1', 'unique': 'True', 'max_length': '1'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['common'] \ No newline at end of file diff --git a/apps/common/migrations/__init__.py b/apps/common/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/common/models.py b/apps/common/models.py index effe8196ed..71304ae132 100644 --- a/apps/common/models.py +++ b/apps/common/models.py @@ -2,6 +2,7 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext from django.contrib.auth.models import AnonymousUser +from django.contrib.auth.models import User SINGLETON_LOCK_ID = 1 @@ -21,8 +22,9 @@ class Singleton(models.Model): self.id = 1 super(Singleton, self).save(*args, **kwargs) - def delete(self): - pass + def delete(self, force=False, *args, **kwargs): + if force: + return super(Singleton, self).delete(*args, **kwargs) class Meta: abstract = True @@ -45,3 +47,11 @@ class AnonymousUserSingleton(Singleton): class Meta: verbose_name = _(u'anonymous user') verbose_name_plural = _(u'anonymous user') + + +class AutoAdminSingleton(Singleton): + auto_admin_account = models.ForeignKey(User, null=True, blank=True, related_name='auto_admin_account', verbose_name=_(u'auto admin')) + auto_admin_password = models.CharField(null=True, blank=True, verbose_name=_(u'auto admin password'), max_length=128) + + class Meta: + verbose_name = verbose_name_plural = _(u'auto admin properties') diff --git a/apps/common/templatetags/autoadmin_tags.py b/apps/common/templatetags/autoadmin_tags.py new file mode 100644 index 0000000000..1170d686f6 --- /dev/null +++ b/apps/common/templatetags/autoadmin_tags.py @@ -0,0 +1,12 @@ +from django.template import Library +from django.utils.importlib import import_module + +from common.models import AutoAdminSingleton + +register = Library() + + +@register.simple_tag(takes_context=True) +def auto_admin_properties(context): + context['auto_admin_properties'] = AutoAdminSingleton.objects.get() + return u'' diff --git a/apps/web_theme/templates/web_theme_login.html b/apps/web_theme/templates/web_theme_login.html index 302e371226..370b0e5c8f 100644 --- a/apps/web_theme/templates/web_theme_login.html +++ b/apps/web_theme/templates/web_theme_login.html @@ -1,6 +1,7 @@ {% extends "web_theme_base.html" %} {% load i18n %} {% load theme_tags %} +{% load autoadmin_tags %} {% block web_theme_head %} {% if user.is_authenticated %} @@ -28,27 +29,42 @@ {% endblock %} {% else %} {% block content_plain %} + {% auto_admin_properties %} + {% if auto_admin_properties.auto_admin_account %}
-

{% block project_name %}{% endblock %}

-

{% trans "Login" %}

+

{% trans "First time login" %}

- {% get_web_theme_setting "VERBOSE_LOGIN" as verbose_login %} - {% if verbose_login %} - {% include "verbose_login.html" %} - {% endif %} + {% endif %} +
+

{% block project_name %}{% endblock %}

+
+

{% trans "Login" %}

+ +
+ {% get_web_theme_setting "VERBOSE_LOGIN" as verbose_login %} + {% if verbose_login %} + {% include "verbose_login.html" %} + {% endif %} {% endblock %} {% endif %} diff --git a/docs/intro/installation.rst b/docs/intro/installation.rst index e3ce3def3d..cf000dd717 100644 --- a/docs/intro/installation.rst +++ b/docs/intro/installation.rst @@ -120,7 +120,7 @@ If using ``PostgreSQL``, enter the following:: Populate the database with the project's schema doing:: - $ ./manage.py syncdb --migrate + $ ./manage.py syncdb --migrate --noinput To test your installation, create a file called settings_local.py with the following content:: @@ -186,7 +186,7 @@ If using ``PostgreSQL``, enter the following:: Populate the database with the project's schema doing:: - $ ./manage.py syncdb --migrate + $ ./manage.py syncdb --migrate --noinput To test your installation, create a file called settings_local.py with the following content:: @@ -276,7 +276,7 @@ To install **Mayan EDMS** on Webfaction_, follow these steps: 6. Create the database schema:: - $ ./manage.py syncdb --migrate + $ ./manage.py syncdb --migrate --noinput 7. Collect the static files of the apps:: diff --git a/docs/releases/0.12.2.rst b/docs/releases/0.12.2.rst index b33ace25a7..d581a85060 100644 --- a/docs/releases/0.12.2.rst +++ b/docs/releases/0.12.2.rst @@ -19,6 +19,9 @@ the diagnosis of installation of runtime error a simple view showing the number of internal interval jobs being used by Mayan EDMS as well as a new app which shows a detail of the current installation enviroment. +#TODO: Add auto admin random password +#TODO: auto admin first run login display + What's new in Mayan EDMS v0.12.2 ================================ @@ -42,9 +45,11 @@ Upgrading from a previous version Start off by adding the new requirements:: $ pip install -r requirements/production.txt - + Migrate existing database schema with:: + $ ./manage.py migrate common 0001 --fake + $ ./manage.py migrate common $ ./manage.py migrate checkouts The upgrade procedure is now complete. diff --git a/docs/topics/settings.rst b/docs/topics/settings.rst index ed9f888596..bc2a654de2 100644 --- a/docs/topics/settings.rst +++ b/docs/topics/settings.rst @@ -454,7 +454,7 @@ Username of the automatically created superuser **COMMON_AUTO_ADMIN_PASSWORD** -Default: ``admin`` +Default: Random generated password Default password of the automatically created superuser