Merge pull request #572 from pcoder/multisite

Task/4104/Multisite and access control
This commit is contained in:
Pcoder 2018-02-22 07:22:05 +01:00 committed by GitHub
commit 059ad0c26a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 190 additions and 16 deletions

View file

@ -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:

View file

@ -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 #
############################################# #############################################

View file

@ -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
View 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,
}),
]

View file

@ -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)

View 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),
),
]

View file

@ -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)

View file

@ -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