diff --git a/.gitignore b/.gitignore index 0eda138c..4ee74b07 100644 --- a/.gitignore +++ b/.gitignore @@ -20,28 +20,6 @@ env # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 -# User-specific stuff: -.idea/workspace.xml -.idea/tasks.xml -.idea/dictionaries -.idea/vcs.xml -.idea/jsLibraryMappings.xml - -# Sensitive or high-churn files: -.idea/dataSources.ids -.idea/dataSources.xml -.idea/dataSources.local.xml -.idea/sqlDataSources.xml -.idea/dynamic.xml -.idea/uiDesigner.xml - -# Gradle: -.idea/gradle.xml -.idea/libraries - -# Mongo Explorer plugin: -.idea/mongoSettings.xml - ## File-based project format: *.iws @@ -49,6 +27,8 @@ env # IntelliJ /out/ +.idea/ +*.iml # mpeltonen/sbt-idea plugin .idea_modules/ diff --git a/Makefile b/Makefile index 2edbe777..7086fdad 100644 --- a/Makefile +++ b/Makefile @@ -43,4 +43,4 @@ dev: clean pre create_static echo 'Loading sample data...' python manage.py runscript sample_data_generator - python manage.py runserver + python manage.py runserver 4440 diff --git a/README.md b/README.md index fda53cde..c5dbf8da 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ Django-based backend API for the OZONE Platform (OZP). For those who just want to get OZP (Center, HUD, Webtop, IWC) up and running, see the [quickstart](https://github.com/ozone-development/ozp-ansible#quickstart) of the [ozp-ansible](https://github.com/ozone-development/ozp-ansible) project. -## 3rd Party Services +## 3rd Party Services Travis-CI -[![Build Status](https://travis-ci.org/aml-development/ozp-backend.svg?branch=master)](https://travis-ci.org/ozone-development/ozp-backend) +[![Build Status](https://travis-ci.org/ozoneplatform/ozp-backend.svg?branch=master)](https://travis-ci.org/ozone-development/ozp-backend) Quantified Code [![Code Issues](https://www.quantifiedcode.com/api/v1/project/13070c3c7b784cf88463f8cee86d5ea2/badge.svg)](https://www.quantifiedcode.com/app/project/13070c3c7b784cf88463f8cee86d5ea2) @@ -30,16 +30,7 @@ of this README, which will create a production-esque deployment of OZP: To serve the application on your host machine with minimal external dependencies, do the following: -1. Remove psycopg2 from requirements.txt (so that Postgres won't be required) -2. Enable HTTP Basic Auth and disable PKI authentication. In settings.py, -`REST_FRAMEWORK.DEFAULT_AUTHENTICATION_CLASSES` should be set to -`'rest_framework.authentication.BasicAuthentication'` -3. Disable the authorization service. In settings.py, set `OZP.USE_AUTH_SERVER` -to `False` -4. In settings.py, set `OZP.DEMO_APP_ROOT` to `localhost:8000` (or wherever -the django app will be served at) - -Then, do the following: +### Windows 1. Install Python 3.4.3. Python can be installed by downloading the appropriate files [here](https://www.python.org/downloads/release/python-343/). Note @@ -52,7 +43,40 @@ Then, do the following: 3. Active the new environment: `source ENV/bin/activate` 4. Install the necessary dependencies into this python environment: `pip install -r requirements.txt` -5. Run the server: `./restart_clean_dev_server.sh` +5. Run the server: `make dev` + +### Debian Linux + +#### Operating system dependencies + +The Python version shipped with Debian Jessie, 3.4.2, will not work with our SDK changes. + +* `apt-get install liblzma-dev libsqlite3-dev sqlite3` +* `wget https://www.python.org/ftp/python/3.5.2/Python-3.5.2.tgz` +* `tar -xzf Python-3.5.2.tgz` +* `cd Python-3.5.2` +* `./configure --enable-loadable-sqlite-extensions` +* `make` +* `sudo make install` +* `python3 -m pip install --upgrade pip` + +#### Development environment preparation + +* `cd ./` +* `git clone https://www.github.com/ozoneplatform/ozp-backend.git` +* `python3 -m venv ozp-venv` +* `source ozp-venv/bin/activate` +* `cd ozp-backend` +* `pip install -r requirements.txt` + +#### Building and running the OZP backend + +* `cd ./` +* `source ozp-venv/bin/activate` +* `cd ozp-backend` +* `make dev` + +## API Documentation Swagger documentation for the api is available at `http://localhost:8000/docs/` Use username `wsmith` password `password` when prompted for authentication info @@ -65,7 +89,7 @@ Run `python release.py` to generate a tarball with Wheels for the application and all of its dependencies. See `release.py` for details ## For Developers -Understanding this project requires knowing a small-medium amount of Django and +Understanding this project requires knowing a moderate amount of Django and a large amount of Django Rest Framework (DRF). From Django itself: * Object-relational mapper (ORM) * Authentication diff --git a/ozp/decorators/cas_decorators.py b/ozp/decorators/cas_decorators.py new file mode 100644 index 00000000..d0807a28 --- /dev/null +++ b/ozp/decorators/cas_decorators.py @@ -0,0 +1,27 @@ +from django.conf import settings +from django.contrib.auth.views import redirect_to_login +from django.http import HttpRequest, HttpResponseForbidden +from django.shortcuts import resolve_url +from django.utils.decorators import available_attrs +from functools import wraps + + +def redirecting_login_required(view_func=None): + """ + Decorator for views that serves as an ajax-aware, drop-in replacement + for login_required. Unauthenticated AJAX requests are rejected as 403 + Forbidden and non-AJAX requests are redirected to a login page. + """ + + @wraps(view_func, assigned=available_attrs(view_func)) + def _wrapped_view(request: HttpRequest, *args, **kwargs): + if request.user.is_authenticated(): + return view_func(request, *args, **kwargs) + if request.is_ajax() or not request.META.get('HTTP_ORIGIN') is None: + return HttpResponseForbidden() + path = request.build_absolute_uri() + resolved_login_url = resolve_url(settings.LOGIN_URL) + return redirect_to_login(path, resolved_login_url) + return _wrapped_view + + diff --git a/ozp/settings.py b/ozp/settings.py index b4b85307..5bc39dd2 100644 --- a/ozp/settings.py +++ b/ozp/settings.py @@ -54,7 +54,10 @@ 'rest_framework_swagger', 'ozpcenter', 'ozpiwc', - 'corsheaders' + 'corsheaders', + + #CAS + 'cas' ) # Note that CorsMiddleware needs to come before Django's CommonMiddleware if @@ -70,10 +73,20 @@ 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', + + #CAS + 'cas.middleware.CASMiddleware' ) ROOT_URLCONF = 'ozp.urls' +# CAS +CAS_SERVER_URL = "http://localhost:9001/cas/" +AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', + 'cas.backends.CASBackend' +) + TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', @@ -172,8 +185,8 @@ 'EXCEPTION_HANDLER': 'ozpcenter.errors.exception_handler', 'DEFAULT_AUTHENTICATION_CLASSES': ( # 'ozpcenter.auth.pkiauth.PkiAuthentication' - 'rest_framework.authentication.BasicAuthentication', - # 'rest_framework.authentication.SessionAuthentication', + # 'rest_framework.authentication.BasicAuthentication', + 'rest_framework.authentication.SessionAuthentication', ), # Use Django's standard `django.contrib.auth` permissions, # or allow read-only access for unauthenticated users. @@ -236,9 +249,9 @@ 'SERVER_CRT': '/ozp/server.crt', 'SERVER_KEY': '/ozp/server.key', # assumes the real URL is /users// - 'USER_INFO_URL': r'http://localhost:8000/demo-auth/users/%s/info.json?issuerDN=%s', + 'USER_INFO_URL': r'http://localhost:4440/demo-auth/users/%s/info.json?issuerDN=%s', # assumes the real URL is /users//groups// - 'USER_GROUPS_URL': r'http://localhost:8000/demo-auth/users/%s/groups/%s/', + 'USER_GROUPS_URL': r'http://localhost:4440/demo-auth/users/%s/groups/%s/', # name of the group in the auth service for apps mall stewards 'APPS_MALL_STEWARD_GROUP_NAME': 'OZP_APPS_MALL_STEWARD', # name of the group in the auth service for org stewards diff --git a/ozp/urls.py b/ozp/urls.py index 280285f3..92c10111 100644 --- a/ozp/urls.py +++ b/ozp/urls.py @@ -18,13 +18,20 @@ from django.conf.urls.static import static from django.contrib import admin +from ozp.decorators.cas_decorators import redirecting_login_required + +from decorator_include import decorator_include urlpatterns = [ url(r'^admin/', include(admin.site.urls)), + url(r'^api/', decorator_include(redirecting_login_required, 'ozpcenter.urls')), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), - url(r'^api/', include('ozpcenter.urls')), url(r'^iwc-api/', include('ozpiwc.urls')), - url(r'^docs/', include('rest_framework_swagger.urls')) + url(r'^docs/', include('rest_framework_swagger.urls')), + + # CAS + url(r'^accounts/login/$', 'cas.views.login', name='login'), + url(r'^accounts/logout/$', 'cas.views.logout', name='logout'), ] # in debug, serve the media and static resources with the django web server diff --git a/ozp/wsgi.py b/ozp/wsgi.py index b4524e3c..0150718d 100644 --- a/ozp/wsgi.py +++ b/ozp/wsgi.py @@ -9,6 +9,7 @@ import os import re +import ssl from django.core.wsgi import get_wsgi_application @@ -31,6 +32,9 @@ def get_version(): raise RuntimeError( "Unable to find version string in {0!s}.".format(VERSION_FILE)) +# This is a hack to disable SSL cert verification +ssl._create_default_https_context = ssl._create_unverified_context + os.environ['OZP_BACKEND_VERSION'] = get_version() # TODO: Find a better way to get version os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ozp.settings") diff --git a/requirements.txt b/requirements.txt index 9409325b..0cf8d650 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,7 +22,6 @@ msgpack-python==0.4.7 nose==1.3.7 pep8==1.7.0 Pillow==2.9.0 -psycopg2==2.6.1 pyflakes==1.0.0 pytz==2015.4 PyYAML==3.11 @@ -30,3 +29,5 @@ redis==2.10.5 requests==2.7.0 six==1.9.0 wheel==0.24.0 +django-decorator-include==1.1 +-e git://nssbu/django-cas.git#egg=django-cas-client-ozp