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/ diff --git a/houdini/houdini/settings.py b/houdini/houdini/settings.py index d98c5a5..ecb6168 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 @@ -38,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') @@ -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'), ) 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/houdini_admin/views.py b/houdini/houdini_admin/views.py index 8f154b6..58d280d 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 @@ -234,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") diff --git a/houdini/houdini_client/auth_backend.py b/houdini/houdini_client/auth_backend.py index d4721f4..32f9e82 100644 --- a/houdini/houdini_client/auth_backend.py +++ b/houdini/houdini_client/auth_backend.py @@ -44,12 +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", - # 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..0a72499 100644 --- a/houdini/houdini_client/views.py +++ b/houdini/houdini_client/views.py @@ -100,12 +100,6 @@ 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 +140,6 @@ 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 +158,6 @@ 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 +204,6 @@ 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 +239,6 @@ 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 +274,6 @@ 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/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() diff --git a/houdini/manage.py b/houdini/manage.py index 4260054..75309db 100755 --- a/houdini/manage.py +++ b/houdini/manage.py @@ -6,13 +6,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "houdini.settings") # set houdini specific env vars - import houdini.config_dev - - # 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) + import houdini.config_production try: from django.core.management import execute_from_command_line 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 }'`