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" | ||||
| 
 | ||||
| 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: "pip install -r requirements.txt" | ||||
| script: | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ Copyright 2015 ungleich. | |||
| # -*- coding: utf-8 -*- | ||||
| # Build paths inside the project like this: os.path.join(BASE_DIR, ...) | ||||
| import os | ||||
| import json | ||||
| 
 | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
| 
 | ||||
|  | @ -54,7 +55,8 @@ PROJECT_DIR = os.path.abspath( | |||
| # load .env file | ||||
| dotenv.read_dotenv("{0}/.env".format(PROJECT_DIR)) | ||||
| 
 | ||||
| SITE_ID = 1 | ||||
| from multisite import SiteID | ||||
| SITE_ID = SiteID(default=1) | ||||
| 
 | ||||
| APP_ROOT_ENDPOINT = "/" | ||||
| APPEND_SLASH = True | ||||
|  | @ -76,6 +78,7 @@ SECRET_KEY = env('DJANGO_SECRET_KEY') | |||
| INSTALLED_APPS = ( | ||||
|     # 1st migrate | ||||
|     'membership', | ||||
|     'djangocms_admin_style', | ||||
|     'django.contrib.admin', | ||||
|     'django.contrib.auth', | ||||
|     'django.contrib.contenttypes', | ||||
|  | @ -83,6 +86,8 @@ INSTALLED_APPS = ( | |||
|     'django.contrib.messages', | ||||
|     'django.contrib.staticfiles', | ||||
|     'django.contrib.sites', | ||||
|     'multisite', | ||||
|     'djangocms_multisite', | ||||
|     'easy_thumbnails', | ||||
|     'utils', | ||||
|     'stored_messages', | ||||
|  | @ -124,7 +129,6 @@ INSTALLED_APPS = ( | |||
|     # 'djangocms_teaser', | ||||
|     'djangocms_page_meta', | ||||
|     'djangocms_text_ckeditor', | ||||
|     'djangocms_admin_style', | ||||
|     'cmsplugin_filer_file', | ||||
|     'cmsplugin_filer_folder', | ||||
|     'cmsplugin_filer_link', | ||||
|  | @ -163,6 +167,8 @@ MIDDLEWARE_CLASSES = ( | |||
|     'cms.middleware.page.CurrentPageMiddleware', | ||||
|     'cms.middleware.toolbar.ToolbarMiddleware', | ||||
|     'cms.middleware.language.LanguageCookieMiddleware', | ||||
|     'multisite.middleware.DynamicSiteMiddleware', | ||||
|     'djangocms_multisite.middleware.CMSMultiSiteMiddleware', | ||||
| ) | ||||
| 
 | ||||
| CSRF_FAILURE_VIEW = 'hosting.views.forbidden_view' | ||||
|  | @ -328,6 +334,8 @@ CMS_PLACEHOLDER_CONF = { | |||
|     }, | ||||
| } | ||||
| 
 | ||||
| CMS_PERMISSION=True | ||||
| 
 | ||||
| CACHES = { | ||||
|     'default': { | ||||
|         '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' | ||||
| 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 # | ||||
| ############################################# | ||||
|  |  | |||
|  | @ -19,5 +19,6 @@ MIDDLEWARE_CLASSES += ("debug_toolbar.middleware.DebugToolbarMiddleware",) | |||
| 
 | ||||
| INSTALLED_APPS += ( | ||||
|     '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.auth.admin import UserAdmin as BaseUserAdmin | ||||
| from django.contrib.auth.forms import ReadOnlyPasswordHashField | ||||
| 
 | ||||
| from .models import CustomUser, StripeCustomer | ||||
| from django.contrib.auth.hashers import make_password | ||||
| 
 | ||||
| 
 | ||||
| class CustomUserAdmin(admin.ModelAdmin): | ||||
|     fields = ('password', 'user_permissions', 'email', 'is_admin') | ||||
| # Refer https://docs.djangoproject.com/en/2.0/topics/auth/customizing/ | ||||
| # for understanding custom auth user model | ||||
| 
 | ||||
|     def save_model(self, request, obj, form, change): | ||||
|         password = form.cleaned_data.get('password') | ||||
| 
 | ||||
|         if not change: | ||||
|             obj.validation_slug = make_password(None) | ||||
| class UserCreationForm(forms.ModelForm): | ||||
|     """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) | ||||
|         obj.save() | ||||
|         return obj | ||||
|     class Meta: | ||||
|         model = CustomUser | ||||
|         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) | ||||
|  |  | |||
							
								
								
									
										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 | ||||
| 
 | ||||
| 
 | ||||
| def get_validation_slug(): | ||||
|     return make_password(None) | ||||
| 
 | ||||
| 
 | ||||
| class CustomUser(AbstractBaseUser, PermissionsMixin): | ||||
|     VALIDATED_CHOICES = ((0, 'Not validated'), (1, 'Validated')) | ||||
|     site = models.ForeignKey(Site, default=1) | ||||
|  | @ -66,8 +70,12 @@ class CustomUser(AbstractBaseUser, PermissionsMixin): | |||
|     email = models.EmailField(unique=True) | ||||
| 
 | ||||
|     validated = models.IntegerField(choices=VALIDATED_CHOICES, default=0) | ||||
|     validation_slug = models.CharField(db_index=True, unique=True, | ||||
|                                        max_length=50) | ||||
|     # By default, we initialize the validation_slug with appropriate value | ||||
|     # 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( | ||||
|         _('staff status'), | ||||
|         default=False, | ||||
|  | @ -171,6 +179,10 @@ class CustomUser(AbstractBaseUser, PermissionsMixin): | |||
|         # Simplest possible answer: All admins are staff | ||||
|         return self.is_admin | ||||
| 
 | ||||
|     @is_staff.setter | ||||
|     def is_staff(self, value): | ||||
|         self._is_staff = value | ||||
| 
 | ||||
| 
 | ||||
| class StripeCustomer(models.Model): | ||||
|     user = models.OneToOneField(CustomUser) | ||||
|  |  | |||
|  | @ -34,6 +34,7 @@ django-meta==1.2 | |||
| django-meta-mixin==0.3.0 | ||||
| django-model-utils==2.5 | ||||
| django-mptt==0.8.4 | ||||
| django-multisite==1.4.1 | ||||
| django-parler==1.6.3 | ||||
| django-phonenumber-field==1.1.0 | ||||
| django-polymorphic==0.9.2 | ||||
|  | @ -97,3 +98,4 @@ billiard==3.5.0.3 | |||
| amqp==2.2.1 | ||||
| vine==1.1.4 | ||||
| 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