Merge pull request #572 from pcoder/multisite
Task/4104/Multisite and access control
This commit is contained in:
		
				commit
				
					
						059ad0c26a
					
				
			
		
					 8 changed files with 190 additions and 16 deletions
				
			
		|  | @ -4,7 +4,7 @@ python: | ||||||
| #   - "3.6" | #   - "3.6" | ||||||
| 
 | 
 | ||||||
| env: | env: | ||||||
|     - DJANGO_SECRET_KEY=0 OPENNEBULA_USERNAME='test' OPENNEBULA_PASSWORD='test' OPENNEBULA_PROTOCOL='http' OPENNEBULA_DOMAIN='test_domain' OPENNEBULA_PORT='2633' OPENNEBULA_ENDPOINT='/RPC2' DCL_TEXT='Data Center Light' CELERY_MAX_RETRIES=0 |     - DJANGO_SECRET_KEY=0 OPENNEBULA_USERNAME='test' OPENNEBULA_PASSWORD='test' OPENNEBULA_PROTOCOL='http' OPENNEBULA_DOMAIN='test_domain' OPENNEBULA_PORT='2633' OPENNEBULA_ENDPOINT='/RPC2' DCL_TEXT='Data Center Light' CELERY_MAX_RETRIES=0 UNGLEICH_SITE_CONFIGS='{"localhost":{"MULTISITE_CMS_URL":"dynamicweb.urls"}}' | ||||||
| # install dependencies | # install dependencies | ||||||
| install: "pip install -r requirements.txt" | install: "pip install -r requirements.txt" | ||||||
| script: | script: | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ Copyright 2015 ungleich. | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| # Build paths inside the project like this: os.path.join(BASE_DIR, ...) | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) | ||||||
| import os | import os | ||||||
|  | import json | ||||||
| 
 | 
 | ||||||
| from django.utils.translation import ugettext_lazy as _ | from django.utils.translation import ugettext_lazy as _ | ||||||
| 
 | 
 | ||||||
|  | @ -54,7 +55,8 @@ PROJECT_DIR = os.path.abspath( | ||||||
| # load .env file | # load .env file | ||||||
| dotenv.read_dotenv("{0}/.env".format(PROJECT_DIR)) | dotenv.read_dotenv("{0}/.env".format(PROJECT_DIR)) | ||||||
| 
 | 
 | ||||||
| SITE_ID = 1 | from multisite import SiteID | ||||||
|  | SITE_ID = SiteID(default=1) | ||||||
| 
 | 
 | ||||||
| APP_ROOT_ENDPOINT = "/" | APP_ROOT_ENDPOINT = "/" | ||||||
| APPEND_SLASH = True | APPEND_SLASH = True | ||||||
|  | @ -76,6 +78,7 @@ SECRET_KEY = env('DJANGO_SECRET_KEY') | ||||||
| INSTALLED_APPS = ( | INSTALLED_APPS = ( | ||||||
|     # 1st migrate |     # 1st migrate | ||||||
|     'membership', |     'membership', | ||||||
|  |     'djangocms_admin_style', | ||||||
|     'django.contrib.admin', |     'django.contrib.admin', | ||||||
|     'django.contrib.auth', |     'django.contrib.auth', | ||||||
|     'django.contrib.contenttypes', |     'django.contrib.contenttypes', | ||||||
|  | @ -83,6 +86,8 @@ INSTALLED_APPS = ( | ||||||
|     'django.contrib.messages', |     'django.contrib.messages', | ||||||
|     'django.contrib.staticfiles', |     'django.contrib.staticfiles', | ||||||
|     'django.contrib.sites', |     'django.contrib.sites', | ||||||
|  |     'multisite', | ||||||
|  |     'djangocms_multisite', | ||||||
|     'easy_thumbnails', |     'easy_thumbnails', | ||||||
|     'utils', |     'utils', | ||||||
|     'stored_messages', |     'stored_messages', | ||||||
|  | @ -124,7 +129,6 @@ INSTALLED_APPS = ( | ||||||
|     # 'djangocms_teaser', |     # 'djangocms_teaser', | ||||||
|     'djangocms_page_meta', |     'djangocms_page_meta', | ||||||
|     'djangocms_text_ckeditor', |     'djangocms_text_ckeditor', | ||||||
|     'djangocms_admin_style', |  | ||||||
|     'cmsplugin_filer_file', |     'cmsplugin_filer_file', | ||||||
|     'cmsplugin_filer_folder', |     'cmsplugin_filer_folder', | ||||||
|     'cmsplugin_filer_link', |     'cmsplugin_filer_link', | ||||||
|  | @ -163,6 +167,8 @@ MIDDLEWARE_CLASSES = ( | ||||||
|     'cms.middleware.page.CurrentPageMiddleware', |     'cms.middleware.page.CurrentPageMiddleware', | ||||||
|     'cms.middleware.toolbar.ToolbarMiddleware', |     'cms.middleware.toolbar.ToolbarMiddleware', | ||||||
|     'cms.middleware.language.LanguageCookieMiddleware', |     'cms.middleware.language.LanguageCookieMiddleware', | ||||||
|  |     'multisite.middleware.DynamicSiteMiddleware', | ||||||
|  |     'djangocms_multisite.middleware.CMSMultiSiteMiddleware', | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| CSRF_FAILURE_VIEW = 'hosting.views.forbidden_view' | CSRF_FAILURE_VIEW = 'hosting.views.forbidden_view' | ||||||
|  | @ -328,6 +334,8 @@ CMS_PLACEHOLDER_CONF = { | ||||||
|     }, |     }, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | CMS_PERMISSION=True | ||||||
|  | 
 | ||||||
| CACHES = { | CACHES = { | ||||||
|     'default': { |     'default': { | ||||||
|         'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', |         'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', | ||||||
|  | @ -507,6 +515,36 @@ STRIPE_API_PRIVATE_KEY_TEST = env('STRIPE_API_PRIVATE_KEY_TEST') | ||||||
| ANONYMOUS_USER_NAME = 'anonymous@ungleich.ch' | ANONYMOUS_USER_NAME = 'anonymous@ungleich.ch' | ||||||
| GUARDIAN_GET_INIT_ANONYMOUS_USER = 'membership.models.get_anonymous_user_instance' | GUARDIAN_GET_INIT_ANONYMOUS_USER = 'membership.models.get_anonymous_user_instance' | ||||||
| 
 | 
 | ||||||
|  | UNGLEICH_SITE_CONFIGS = env('UNGLEICH_SITE_CONFIGS') | ||||||
|  | 
 | ||||||
|  | MULTISITE_CMS_URLS = {} | ||||||
|  | if UNGLEICH_SITE_CONFIGS == "": | ||||||
|  |     raise Exception("Please define UNGLEICH_SITE_CONFIGS in your .env") | ||||||
|  | else: | ||||||
|  |     try: | ||||||
|  |         configs_dict=json.loads(UNGLEICH_SITE_CONFIGS) | ||||||
|  |     except ValueError as verr: | ||||||
|  |         raise Exception("UNGLEICH_SITE_CONFIGS is not a valid JSON: {}".format( | ||||||
|  |             str(verr) | ||||||
|  |         )) | ||||||
|  |     else: | ||||||
|  |         MULTISITE_CMS_URLS = { | ||||||
|  |             k:v['MULTISITE_CMS_URL'] for (k,v) in configs_dict.items() | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | MULTISITE_CMS_ALIASES = { | ||||||
|  | } | ||||||
|  | MULTISITE_CMS_FALLBACK = env('MULTISITE_CMS_FALLBACK') | ||||||
|  | if MULTISITE_CMS_FALLBACK == '': | ||||||
|  |     MULTISITE_CMS_FALLBACK = 'datacenterlight.ch' | ||||||
|  | MULTISITE_FALLBACK = 'django.views.generic.base.RedirectView' | ||||||
|  | MULTISITE_FALLBACK_KWARGS = { | ||||||
|  |     'url': 'https://{}/'.format(MULTISITE_CMS_FALLBACK), 'permanent': False | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FILER_ENABLE_PERMISSIONS = True | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ############################################# | ############################################# | ||||||
| # configurations for opennebula-integration # | # configurations for opennebula-integration # | ||||||
| ############################################# | ############################################# | ||||||
|  |  | ||||||
|  | @ -19,5 +19,6 @@ MIDDLEWARE_CLASSES += ("debug_toolbar.middleware.DebugToolbarMiddleware",) | ||||||
| 
 | 
 | ||||||
| INSTALLED_APPS += ( | INSTALLED_APPS += ( | ||||||
|     'django_extensions', |     'django_extensions', | ||||||
|     'debug_toolbar' |     # debug_toolbar seems to conflict with multisite (and djangocms_multisite) | ||||||
|  |     #   'debug_toolbar' | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								dynamicweb/urls_multi.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								dynamicweb/urls_multi.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | from django.conf import settings | ||||||
|  | from django.conf.urls import include, url | ||||||
|  | from django.conf.urls.i18n import i18n_patterns | ||||||
|  | from django.contrib import admin | ||||||
|  | from django.views import static as static_view | ||||||
|  | 
 | ||||||
|  | urlpatterns = i18n_patterns( | ||||||
|  |     url(r'^admin/', include(admin.site.urls)), | ||||||
|  |     url(r'^ncms/', include('cms.urls')), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | urlpatterns += [ | ||||||
|  |     url(r'^media/(?P<path>.*)$', | ||||||
|  |         static_view.serve, { | ||||||
|  |             'document_root': settings.MEDIA_ROOT, | ||||||
|  |         }), | ||||||
|  | ] | ||||||
|  | @ -1,20 +1,103 @@ | ||||||
|  | from django import forms | ||||||
| from django.contrib import admin | from django.contrib import admin | ||||||
|  | from django.contrib.auth.admin import UserAdmin as BaseUserAdmin | ||||||
|  | from django.contrib.auth.forms import ReadOnlyPasswordHashField | ||||||
|  | 
 | ||||||
| from .models import CustomUser, StripeCustomer | from .models import CustomUser, StripeCustomer | ||||||
| from django.contrib.auth.hashers import make_password |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CustomUserAdmin(admin.ModelAdmin): | # Refer https://docs.djangoproject.com/en/2.0/topics/auth/customizing/ | ||||||
|     fields = ('password', 'user_permissions', 'email', 'is_admin') | # for understanding custom auth user model | ||||||
| 
 | 
 | ||||||
|     def save_model(self, request, obj, form, change): |  | ||||||
|         password = form.cleaned_data.get('password') |  | ||||||
| 
 | 
 | ||||||
|         if not change: | class UserCreationForm(forms.ModelForm): | ||||||
|             obj.validation_slug = make_password(None) |     """A form for creating new users. Includes all the required | ||||||
|  |     fields, plus a repeated password.""" | ||||||
|  |     password1 = forms.CharField(label='Password', widget=forms.PasswordInput) | ||||||
|  |     password2 = forms.CharField(label='Password confirmation', | ||||||
|  |                                 widget=forms.PasswordInput) | ||||||
| 
 | 
 | ||||||
|         obj.set_password(password) |     class Meta: | ||||||
|         obj.save() |         model = CustomUser | ||||||
|         return obj |         fields = ('email', 'user_permissions', 'email', 'is_admin') | ||||||
|  | 
 | ||||||
|  |     def clean_password2(self): | ||||||
|  |         # Check that the two password entries match | ||||||
|  |         password1 = self.cleaned_data.get("password1") | ||||||
|  |         password2 = self.cleaned_data.get("password2") | ||||||
|  |         if password1 and password2 and password1 != password2: | ||||||
|  |             raise forms.ValidationError("Passwords don't match") | ||||||
|  |         return password2 | ||||||
|  | 
 | ||||||
|  |     def save(self, commit=True): | ||||||
|  |         # Save the provided password in hashed format | ||||||
|  |         user = super().save(commit=False) | ||||||
|  |         user.set_password(self.cleaned_data["password1"]) | ||||||
|  |         if commit: | ||||||
|  |             user.save() | ||||||
|  |         return user | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class UserChangeForm(forms.ModelForm): | ||||||
|  |     """A form for updating users. Includes all the fields on | ||||||
|  |     the user, but replaces the password field with admin's | ||||||
|  |     password hash display field. | ||||||
|  |     """ | ||||||
|  |     password = ReadOnlyPasswordHashField( | ||||||
|  |         label="Password", | ||||||
|  |         help_text=( | ||||||
|  |             "Raw passwords are not stored, so there is no way to see " | ||||||
|  |             "this user's password, but you can change the password " | ||||||
|  |             "using <a href=\"../password/\">this form</a>.") | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         model = CustomUser | ||||||
|  |         fields = ('email', 'password', 'is_admin') | ||||||
|  | 
 | ||||||
|  |     def clean_password(self): | ||||||
|  |         # Regardless of what the user provides, return the initial value. | ||||||
|  |         # This is done here, rather than on the field, because the | ||||||
|  |         # field does not have access to the initial value | ||||||
|  |         return self.initial["password"] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class CustomUserAdmin(BaseUserAdmin): | ||||||
|  |     # The forms to add and change user instances | ||||||
|  |     form = UserChangeForm | ||||||
|  |     add_form = UserCreationForm | ||||||
|  | 
 | ||||||
|  |     # The fields to be used in displaying the User model. | ||||||
|  |     # These override the definitions on the base UserAdmin | ||||||
|  |     # that reference specific fields on auth.User. | ||||||
|  |     list_display = ( | ||||||
|  |         'email', 'is_admin', 'is_superuser' | ||||||
|  |     ) | ||||||
|  |     list_filter = () | ||||||
|  |     fieldsets = ( | ||||||
|  |         (None, {'fields': ('email',)}), | ||||||
|  |         ('Change Password', | ||||||
|  |          {'fields': ('password',), | ||||||
|  |           'description': "Raw passwords are not stored, so there is no way to " | ||||||
|  |                          "see this user's password, but you can change the " | ||||||
|  |                          "password using <a href=\"../password/\">this " | ||||||
|  |                          "form</a>." | ||||||
|  |           } | ||||||
|  |          ), | ||||||
|  |         ('Permissions', {'fields': ('is_admin', 'user_permissions', | ||||||
|  |                                     'groups')}), | ||||||
|  |     ) | ||||||
|  |     # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin | ||||||
|  |     # overrides get_fieldsets to use this attribute when creating a user. | ||||||
|  |     add_fieldsets = ( | ||||||
|  |         (None, { | ||||||
|  |             'classes': ('wide',), | ||||||
|  |             'fields': ('email', 'password1', 'password2')} | ||||||
|  |          ), | ||||||
|  |     ) | ||||||
|  |     search_fields = ('email',) | ||||||
|  |     ordering = ('email',) | ||||||
|  |     filter_horizontal = () | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| admin.site.register(CustomUser, CustomUserAdmin) | admin.site.register(CustomUser, CustomUserAdmin) | ||||||
|  |  | ||||||
							
								
								
									
										21
									
								
								membership/migrations/0007_auto_20180213_0128.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								membership/migrations/0007_auto_20180213_0128.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Generated by Django 1.9.4 on 2018-02-13 01:28 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | import membership.models | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('membership', '0006_auto_20160526_0445'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='customuser', | ||||||
|  |             name='validation_slug', | ||||||
|  |             field=models.CharField(db_index=True, default=membership.models.get_validation_slug, max_length=50, unique=True), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
|  | @ -59,6 +59,10 @@ class MyUserManager(BaseUserManager): | ||||||
|         return user |         return user | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def get_validation_slug(): | ||||||
|  |     return make_password(None) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class CustomUser(AbstractBaseUser, PermissionsMixin): | class CustomUser(AbstractBaseUser, PermissionsMixin): | ||||||
|     VALIDATED_CHOICES = ((0, 'Not validated'), (1, 'Validated')) |     VALIDATED_CHOICES = ((0, 'Not validated'), (1, 'Validated')) | ||||||
|     site = models.ForeignKey(Site, default=1) |     site = models.ForeignKey(Site, default=1) | ||||||
|  | @ -66,8 +70,12 @@ class CustomUser(AbstractBaseUser, PermissionsMixin): | ||||||
|     email = models.EmailField(unique=True) |     email = models.EmailField(unique=True) | ||||||
| 
 | 
 | ||||||
|     validated = models.IntegerField(choices=VALIDATED_CHOICES, default=0) |     validated = models.IntegerField(choices=VALIDATED_CHOICES, default=0) | ||||||
|     validation_slug = models.CharField(db_index=True, unique=True, |     # By default, we initialize the validation_slug with appropriate value | ||||||
|                                        max_length=50) |     # This is required for User(page) admin | ||||||
|  |     validation_slug = models.CharField( | ||||||
|  |         db_index=True, unique=True, max_length=50, | ||||||
|  |         default=get_validation_slug | ||||||
|  |     ) | ||||||
|     is_admin = models.BooleanField( |     is_admin = models.BooleanField( | ||||||
|         _('staff status'), |         _('staff status'), | ||||||
|         default=False, |         default=False, | ||||||
|  | @ -171,6 +179,10 @@ class CustomUser(AbstractBaseUser, PermissionsMixin): | ||||||
|         # Simplest possible answer: All admins are staff |         # Simplest possible answer: All admins are staff | ||||||
|         return self.is_admin |         return self.is_admin | ||||||
| 
 | 
 | ||||||
|  |     @is_staff.setter | ||||||
|  |     def is_staff(self, value): | ||||||
|  |         self._is_staff = value | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class StripeCustomer(models.Model): | class StripeCustomer(models.Model): | ||||||
|     user = models.OneToOneField(CustomUser) |     user = models.OneToOneField(CustomUser) | ||||||
|  |  | ||||||
|  | @ -34,6 +34,7 @@ django-meta==1.2 | ||||||
| django-meta-mixin==0.3.0 | django-meta-mixin==0.3.0 | ||||||
| django-model-utils==2.5 | django-model-utils==2.5 | ||||||
| django-mptt==0.8.4 | django-mptt==0.8.4 | ||||||
|  | django-multisite==1.4.1 | ||||||
| django-parler==1.6.3 | django-parler==1.6.3 | ||||||
| django-phonenumber-field==1.1.0 | django-phonenumber-field==1.1.0 | ||||||
| django-polymorphic==0.9.2 | django-polymorphic==0.9.2 | ||||||
|  | @ -97,3 +98,4 @@ billiard==3.5.0.3 | ||||||
| amqp==2.2.1 | amqp==2.2.1 | ||||||
| vine==1.1.4 | vine==1.1.4 | ||||||
| cdist==4.7.0 | cdist==4.7.0 | ||||||
|  | git+https://github.com/ungleich/djangocms-multisite.git#egg=djangocms_multisite | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue