From 57961a80ebb53a8ddf723cf09eed6947c015a0eb Mon Sep 17 00:00:00 2001 From: peterthejohnston Date: Sat, 3 Jun 2017 13:37:38 -0600 Subject: [PATCH 01/13] resolve merge conflicts --- houdini/houdini/wsgi.py | 2 +- houdini/manage.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/houdini/houdini/wsgi.py b/houdini/houdini/wsgi.py index f88db84..09b1da1 100644 --- a/houdini/houdini/wsgi.py +++ b/houdini/houdini/wsgi.py @@ -14,6 +14,6 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "houdini.settings") # set houdini specific env vars -import houdini.config_dev +import houdini.config_production application = get_wsgi_application() diff --git a/houdini/manage.py b/houdini/manage.py index 4260054..491b9e8 100755 --- a/houdini/manage.py +++ b/houdini/manage.py @@ -6,7 +6,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "houdini.settings") # set houdini specific env vars - import houdini.config_dev + import houdini.config_production # TODO: only in development!!! # disable SSL warnings so we can use a self-signed cert From d183d6443821a7b6bd400eaaa30eb0b7635e1635 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 2 Jun 2017 19:51:18 +0000 Subject: [PATCH 02/13] start, stop, restart bash scripts --- restart.sh | 3 +++ start.sh | 5 +++++ stop.sh | 2 ++ 3 files changed, 10 insertions(+) create mode 100755 restart.sh create mode 100755 start.sh create mode 100755 stop.sh diff --git a/restart.sh b/restart.sh new file mode 100755 index 0000000..6a4f372 --- /dev/null +++ b/restart.sh @@ -0,0 +1,3 @@ +#!/bin/bash +/home/thecorp/django/houdini/stop.sh +/home/thecorp/django/houdini/start.sh diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..854216d --- /dev/null +++ b/start.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +cd /home/thecorp/django/houdini/houdini + +../virt_env/bin/gunicorn -w 8 -b 127.0.0.1:8080 --log-file /tmp/houdini.log -D houdini.wsgi:application diff --git a/stop.sh b/stop.sh new file mode 100755 index 0000000..16334c3 --- /dev/null +++ b/stop.sh @@ -0,0 +1,2 @@ +#!/bin/bash +kill -9 `ps -aux | grep gunicorn | grep houdini.wsgi:application | awk '{ print $2 }'` From dbaa0657dd4019e7190543d996dbe064f016627f Mon Sep 17 00:00:00 2001 From: peterthejohnston Date: Fri, 2 Jun 2017 14:06:52 -0600 Subject: [PATCH 03/13] add static_root to settings --- houdini/houdini/settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/houdini/houdini/settings.py b/houdini/houdini/settings.py index d98c5a5..cc86928 100644 --- a/houdini/houdini/settings.py +++ b/houdini/houdini/settings.py @@ -17,6 +17,8 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +ROOT = os.path.dirname(BASE_DIR) +STATIC_ROOT = os.path.join(ROOT, 'static') # Quick-start development settings - unsuitable for production From 6ed65ab18378f77315245cd560cc2ec0bfff1b24 Mon Sep 17 00:00:00 2001 From: peterthejohnston Date: Fri, 2 Jun 2017 14:07:21 -0600 Subject: [PATCH 04/13] test without --- houdini/houdini/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/houdini/houdini/settings.py b/houdini/houdini/settings.py index cc86928..9430de8 100644 --- a/houdini/houdini/settings.py +++ b/houdini/houdini/settings.py @@ -17,8 +17,8 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -ROOT = os.path.dirname(BASE_DIR) -STATIC_ROOT = os.path.join(ROOT, 'static') +# ROOT = os.path.dirname(BASE_DIR) +# STATIC_ROOT = os.path.join(ROOT, 'static') # Quick-start development settings - unsuitable for production From 07fee1eeee8fa36cba094baa7ab0151b1d41929f Mon Sep 17 00:00:00 2001 From: peterthejohnston Date: Fri, 2 Jun 2017 14:07:49 -0600 Subject: [PATCH 05/13] jk we needed it --- houdini/houdini/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/houdini/houdini/settings.py b/houdini/houdini/settings.py index 9430de8..cc86928 100644 --- a/houdini/houdini/settings.py +++ b/houdini/houdini/settings.py @@ -17,8 +17,8 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -# ROOT = os.path.dirname(BASE_DIR) -# STATIC_ROOT = os.path.join(ROOT, 'static') +ROOT = os.path.dirname(BASE_DIR) +STATIC_ROOT = os.path.join(ROOT, 'static') # Quick-start development settings - unsuitable for production From a7b124a71c6bf63d52c2f893d4a0da37c401c83c Mon Sep 17 00:00:00 2001 From: peterthejohnston Date: Fri, 2 Jun 2017 14:32:53 -0600 Subject: [PATCH 06/13] remove unsafe python requests --- houdini/houdini_client/auth_backend.py | 5 ----- houdini/houdini_client/views.py | 30 -------------------------- houdini/manage.py | 6 ------ 3 files changed, 41 deletions(-) diff --git a/houdini/houdini_client/auth_backend.py b/houdini/houdini_client/auth_backend.py index d4721f4..8809322 100644 --- a/houdini/houdini_client/auth_backend.py +++ b/houdini/houdini_client/auth_backend.py @@ -44,12 +44,7 @@ def authenticate(self, email=None, password=None, response=None): # POST it to the login endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/login", - # TODO: cert and verify will change in production - # cert isn't necessary since we have verify=False, but we will leave it - # as a placeholder for when we are in production with Let's Encrypt cert=settings.SSL_DEV_CERT_KEY, - verify=False, - # TODO: ^only in development!!! data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string diff --git a/houdini/houdini_client/views.py b/houdini/houdini_client/views.py index 5c007a0..f2c5954 100644 --- a/houdini/houdini_client/views.py +++ b/houdini/houdini_client/views.py @@ -100,12 +100,7 @@ def register(request): # POST it to the create_user endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/create_user", - # TODO: cert and verify will change in production - # cert isn't necessary since we have verify=False, but we will leave it - # as a placeholder for when we are in production with Let's Encrypt cert=settings.SSL_DEV_CERT_KEY, - verify=False, - # TODO: ^only in development!!! data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string @@ -146,12 +141,7 @@ def activate(request, key): # POST it to the regenerate_activation_key endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/regenerate_activation_key", - # TODO: cert and verify will change in production - # cert isn't necessary since we have verify=False, but we will leave it - # as a placeholder for when we are in production with Let's Encrypt cert=settings.SSL_DEV_CERT_KEY, - verify=False, - # TODO: ^only in development!!! data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string @@ -170,12 +160,7 @@ def activate(request, key): # POST it to the activate_user endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/activate_user", - # TODO: cert and verify will change in production - # cert isn't necessary since we have verify=False, but we will leave it - # as a placeholder for when we are in production with Let's Encrypt cert=settings.SSL_DEV_CERT_KEY, - verify=False, - # TODO: ^only in development!!! data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string @@ -222,12 +207,7 @@ def password_change(request): # POST it to the password_change endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/password_change", - # TODO: cert and verify will change in production - # cert isn't necessary since we have verify=False, but we will leave it - # as a placeholder for when we are in production with Let's Encrypt cert=settings.SSL_DEV_CERT_KEY, - verify=False, - # TODO: ^only in development!!! data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string @@ -263,12 +243,7 @@ def password_reset(request): # POST it to the password_reset endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/password_reset", - # TODO: cert and verify will change in production - # cert isn't necessary since we have verify=False, but we will leave it - # as a placeholder for when we are in production with Let's Encrypt cert=settings.SSL_DEV_CERT_KEY, - verify=False, - # TODO: ^only in development!!! data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string @@ -304,12 +279,7 @@ def password_set(request, key): # POST it to the password_set endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/password_set", - # TODO: cert and verify will change in production - # cert isn't necessary since we have verify=False, but we will leave it - # as a placeholder for when we are in production with Let's Encrypt cert=settings.SSL_DEV_CERT_KEY, - verify=False, - # TODO: ^only in development!!! data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string diff --git a/houdini/manage.py b/houdini/manage.py index 491b9e8..75309db 100755 --- a/houdini/manage.py +++ b/houdini/manage.py @@ -8,12 +8,6 @@ # set houdini specific env vars import houdini.config_production - # TODO: only in development!!! - # disable SSL warnings so we can use a self-signed cert - import requests - from requests.packages.urllib3.exceptions import InsecureRequestWarning - requests.packages.urllib3.disable_warnings(InsecureRequestWarning) - try: from django.core.management import execute_from_command_line except ImportError: From cd7a732a8206cc5785984d1361a4f5cc97653278 Mon Sep 17 00:00:00 2001 From: peterthejohnston Date: Fri, 2 Jun 2017 14:33:55 -0600 Subject: [PATCH 07/13] remove collected static files from git --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d5ab607..f090bed 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ virt_env/ *.pyc .DS_Store houdini/houdini/config*.py +static/ From 39b1ebac9e811a50783b7c9600ab3a0312000826 Mon Sep 17 00:00:00 2001 From: peterthejohnston Date: Fri, 2 Jun 2017 14:40:26 -0600 Subject: [PATCH 08/13] test python requests POSTing --- houdini/houdini_client/auth_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/houdini/houdini_client/auth_backend.py b/houdini/houdini_client/auth_backend.py index 8809322..014f682 100644 --- a/houdini/houdini_client/auth_backend.py +++ b/houdini/houdini_client/auth_backend.py @@ -44,7 +44,7 @@ def authenticate(self, email=None, password=None, response=None): # POST it to the login endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/login", - cert=settings.SSL_DEV_CERT_KEY, + # cert=settings.SSL_DEV_CERT_KEY, data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string From faf91cb38aef00be58ee55c0af042421e6b7face Mon Sep 17 00:00:00 2001 From: peterthejohnston Date: Fri, 2 Jun 2017 14:44:25 -0600 Subject: [PATCH 09/13] remove self-signed ssl cert --- houdini/houdini/settings.py | 2 -- houdini/houdini_client/auth_backend.py | 1 - houdini/houdini_client/views.py | 6 ------ 3 files changed, 9 deletions(-) diff --git a/houdini/houdini/settings.py b/houdini/houdini/settings.py index cc86928..0993eb4 100644 --- a/houdini/houdini/settings.py +++ b/houdini/houdini/settings.py @@ -40,8 +40,6 @@ SECURE_HSTS_SECONDS = 600 # TODO: should eventually be set to something like 31536000 (1 year) SECURE_HSTS_INCLUDE_SUBDOMAINS = True -SSL_DEV_CERT_KEY = (os.getenv('SSL_DEV_CERT'), os.getenv('SSL_DEV_KEY')) - HOUDINI_KEY = os.getenv('app_key') HOUDINI_SECRET = os.getenv('app_secret') HOUDINI_SERVER = os.getenv('houdini_server') diff --git a/houdini/houdini_client/auth_backend.py b/houdini/houdini_client/auth_backend.py index 014f682..32f9e82 100644 --- a/houdini/houdini_client/auth_backend.py +++ b/houdini/houdini_client/auth_backend.py @@ -44,7 +44,6 @@ def authenticate(self, email=None, password=None, response=None): # POST it to the login endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/login", - # cert=settings.SSL_DEV_CERT_KEY, data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string diff --git a/houdini/houdini_client/views.py b/houdini/houdini_client/views.py index f2c5954..0a72499 100644 --- a/houdini/houdini_client/views.py +++ b/houdini/houdini_client/views.py @@ -100,7 +100,6 @@ def register(request): # POST it to the create_user endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/create_user", - cert=settings.SSL_DEV_CERT_KEY, data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string @@ -141,7 +140,6 @@ def activate(request, key): # POST it to the regenerate_activation_key endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/regenerate_activation_key", - cert=settings.SSL_DEV_CERT_KEY, data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string @@ -160,7 +158,6 @@ def activate(request, key): # POST it to the activate_user endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/activate_user", - cert=settings.SSL_DEV_CERT_KEY, data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string @@ -207,7 +204,6 @@ def password_change(request): # POST it to the password_change endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/password_change", - cert=settings.SSL_DEV_CERT_KEY, data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string @@ -243,7 +239,6 @@ def password_reset(request): # POST it to the password_reset endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/password_reset", - cert=settings.SSL_DEV_CERT_KEY, data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string @@ -279,7 +274,6 @@ def password_set(request, key): # POST it to the password_set endpoint r = requests.post( settings.HOUDINI_SERVER + "/endpoints/password_set", - cert=settings.SSL_DEV_CERT_KEY, data={ "app_key": settings.HOUDINI_KEY, "jwt_string": jwt_string From bf6fa85cb492beb91575fedb5e7db79c5afab02b Mon Sep 17 00:00:00 2001 From: peterthejohnston Date: Sat, 3 Jun 2017 13:43:45 -0600 Subject: [PATCH 10/13] resolve merge conflicts --- houdini/houdini_admin/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/houdini/houdini_admin/views.py b/houdini/houdini_admin/views.py index 8f154b6..75a3424 100644 --- a/houdini/houdini_admin/views.py +++ b/houdini/houdini_admin/views.py @@ -4,6 +4,7 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.http import HttpResponse from django.shortcuts import redirect, render +from django.utils.decorators import method_decorator from django.views.generic.edit import UpdateView from django.utils.decorators import method_decorator From c6e7ab35a1eda1e7bb253af7da687a791ce70ea7 Mon Sep 17 00:00:00 2001 From: peterthejohnston Date: Fri, 2 Jun 2017 15:04:52 -0600 Subject: [PATCH 11/13] add back test views --- houdini/houdini_admin/views.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/houdini/houdini_admin/views.py b/houdini/houdini_admin/views.py index 75a3424..58d280d 100644 --- a/houdini/houdini_admin/views.py +++ b/houdini/houdini_admin/views.py @@ -235,3 +235,16 @@ def delete_permission(request, pk): if permission_to_delete.delete(): messages.success(request, message) return redirect('permissions') + + +@login_required +def login_test(request): + return render(request, "houdini_admin/login_test.html") + +@role_required('super cool role') +def role_test(request): + return render(request, "houdini_admin/role_test.html") + +@permission_required('even new one') +def permission_test(request): + return render(request, "houdini_admin/permission_test.html") From fb2779f58385aff66215e22aca65666199f1d7a3 Mon Sep 17 00:00:00 2001 From: peterthejohnston Date: Sat, 3 Jun 2017 13:13:58 -0600 Subject: [PATCH 12/13] set DEBUG = False --- houdini/houdini/settings.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/houdini/houdini/settings.py b/houdini/houdini/settings.py index 0993eb4..ecb6168 100644 --- a/houdini/houdini/settings.py +++ b/houdini/houdini/settings.py @@ -57,12 +57,13 @@ EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD') # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = False -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ['auth.thecorp.org'] ADMINS = ( ('Justice Suh', 'justice.suh@gmail.com'), + ('Peter Johnston', 'peter@thecorp.org'), ) From a65675895b209be74078ef6b48b96d513f2b2eb4 Mon Sep 17 00:00:00 2001 From: Nick Chapman Date: Wed, 26 Jul 2017 20:41:41 +0200 Subject: [PATCH 13/13] Resolve issue with @role_required(SOME_INHERITED_ROLE) --- houdini/houdini_server/endpoints.py | 6 ++++- houdini/houdini_server/models.py | 42 +++++++++++++++++++++++++++-- houdini/houdini_server/signals.py | 13 +++++++-- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/houdini/houdini_server/endpoints.py b/houdini/houdini_server/endpoints.py index 2c37c11..e852bae 100644 --- a/houdini/houdini_server/endpoints.py +++ b/houdini/houdini_server/endpoints.py @@ -88,7 +88,11 @@ def post(self, request): app_roles = set(self.app.roles.all()) # Get all of the roles the user has user_roles = set(user.roles.all()) - relevant_roles = app_roles.intersection(user_roles) + user_app_roles = app_roles.intersection(user_roles) + # Now get all of the roles that they have and inherit + relevant_roles = set() + for role in user_app_roles: + relevant_roles.add(role.get_all_inherited_roles()) # Now get all of the permissions for every role permissions = set() for role in relevant_roles: diff --git a/houdini/houdini_server/models.py b/houdini/houdini_server/models.py index c5390f6..cb9cceb 100644 --- a/houdini/houdini_server/models.py +++ b/houdini/houdini_server/models.py @@ -139,6 +139,7 @@ def get_parent_slugs_for_role(self): return [parent.slug for parent in self.parents] def get_parent_permissions(self): + # TODO: This could be sped up by using something to prevent iterating across the same parent multiple times parent_permissions = set() search_queue = Queue() for parent in self.parents.all(): @@ -156,6 +157,19 @@ def get_all_permissions(self): permissions |= self.get_parent_permissions() return permissions + def get_all_inherited_roles(self): + parents = set() + search_queue = Queue() + for parent in self.parents.all(): + search_queue.put(parent) + while not search_queue.empty(): + parent = search_queue.get() + parents.add(parent) + for role_parent in parent.parents.all(): + if role_parent not in parents: + search_queue.put(parent) + + def __str__(self): return self.name @@ -182,6 +196,28 @@ def refresh_table(): mapping.save() +class RolesToParents(models.Model): + role = models.ForeignKey('Role') + parents = models.TextField() + + @staticmethod + def refresh_table(): + """ + Clears the entire table and for every role finds all of its parents + :return: void + """ + # First clear the entire table + RolesToParents.objects.all().delete() + # Now regenerate all of the mappings + for role in Role.objects.all(): + parents = role.get_all_inherited_roles() + parents_list = [parent.name for parent in parents] + parents_list += [parent.slug for parent in parents] + parents_string = json.dumps(parents_list) + mapping = RolesToParents(role=role, parents=parents_string) + mapping.save() + + class HoudiniUserManager(UserManager): def get_by_natural_key(self, email): return self.get(email=email) @@ -233,9 +269,11 @@ def generate_activation_key(self): def send_activation_email(self, activate_url, resend=False): activation_link = activate_url + self.activation_key if resend: - message = "Hello " + str(self) + "! Your old activation key expired so we have generated a new one for you. Go to this link to activate your account: " + activation_link + message = "Hello " + str( + self) + "! Your old activation key expired so we have generated a new one for you. Go to this link to activate your account: " + activation_link else: - message = "Hello " + str(self) + "! You have successfully registered for an account. Go to this link to activate your account: " + activation_link + message = "Hello " + str( + self) + "! You have successfully registered for an account. Go to this link to activate your account: " + activation_link send_mail( "Activate your account", message, diff --git a/houdini/houdini_server/signals.py b/houdini/houdini_server/signals.py index eed7168..4078df5 100644 --- a/houdini/houdini_server/signals.py +++ b/houdini/houdini_server/signals.py @@ -1,19 +1,28 @@ from django.db.models.signals import post_save, m2m_changed, post_delete from django.dispatch import receiver -from .models import Role, RolesToPermissions +from .models import Role, RolesToPermissions, RolesToParents + @receiver(m2m_changed, sender=Role.permissions.through) -def role_m2m_changed(sender, **kwargs): +def role_m2m_permissions_changed(sender, **kwargs): RolesToPermissions.refresh_table() + +@receiver(m2m_changed, sender=Role.parents.through) +def role_m2m_parents_changed(sender, **kwargs): + RolesToParents.refresh_table() + + @receiver(post_delete, sender="houdini_server.Role") def role_post_delete(sender, **kwargs): RolesToPermissions.refresh_table() + @receiver(post_save, sender="houdini_server.Permission") def permission_post_save(sender, **kwargs): RolesToPermissions.refresh_table() + @receiver(post_delete, sender="houdini_server.Permission") def permission_post_delete(sender, **kwargs): RolesToPermissions.refresh_table()