Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
dellis23 committed Feb 9, 2014
0 parents commit fabbec9
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 0 deletions.
41 changes: 41 additions & 0 deletions django_wordpress_auth/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import hmac
import hashlib
import md5
import urllib2

from django.conf import settings
import phpserialize

from models import WpOptions, WpUsers, WpUsermeta


SITE_URL = WpOptions.objects.using('wordpress')\
.get(option_name='siteurl').option_value
COOKIEHASH = md5.new(SITE_URL).hexdigest()
LOGIN_URL = SITE_URL + "/wp-login.php"


def _hmac(salt, data):
return hmac.new(salt, msg=data, digestmod=hashlib.md5).hexdigest()


def _generate_auth_cookie(username, password, expires):
expires = str(expires)
wp_salt = settings.LOGGED_IN_KEY + settings.LOGGED_IN_SALT
pass_fragment = password[8:12]
wp_hash = _hmac(wp_salt, username + pass_fragment + "|" + expires)
auth_cookie = _hmac(wp_hash, username + "|" + expires)
return auth_cookie


def get_wordpress_user(request):
cookie_key = 'wordpress_logged_in_' + COOKIEHASH
cookie_value = request.COOKIES.get(cookie_key)
if not cookie_value:
return None
username, expires, hmac = urllib2.unquote(cookie_value).split('|')
wp_user = WpUsers.objects.using('wordpress').get(login=username)
if hmac == _generate_auth_cookie(username, wp_user.password, expires):
return wp_user
else:
return None
36 changes: 36 additions & 0 deletions django_wordpress_auth/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from django.core.exceptions import PermissionDenied
from django.shortcuts import redirect

from . import get_wordpress_user, LOGIN_URL


def wordpress_login_required(fn, *args, **kwargs):
def wrapped(request, *args, **kwargs):
if not request.wordpress_user:
redirect_to = request.build_absolute_uri(request.path)
return redirect(LOGIN_URL + "?redirect_to=" + redirect_to)
else:
return fn(request, *args, **kwargs)
return wrapped


def wordpress_requires_role(role):
def real_decorator(fn, *args, **kwargs):
def wrapped(request, *args, **kwargs):
if role in request.wordpress_user.roles:
return fn(request, *args, **kwargs)
else:
raise PermissionDenied()
return wrapped
return real_decorator


def wordpress_requires_capability(capability):
def real_decorator(fn, *args, **kwargs):
def wrapped(request, *args, **kwargs):
if capability in request.wordpress_user.capabilities:
return fn(request, *args, **kwargs)
else:
raise PermissionDenied()
return wrapped
return real_decorator
10 changes: 10 additions & 0 deletions django_wordpress_auth/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.utils.functional import SimpleLazyObject

from . import get_wordpress_user


class WordpressAuthMiddleware(object):
def process_request(self, request):
assert hasattr(request, 'session'), "django-wordpress-auth requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."

request.wordpress_user = SimpleLazyObject(lambda: get_wordpress_user(request))
64 changes: 64 additions & 0 deletions django_wordpress_auth/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from django.db import models

import phpserialize


class WpOptions(models.Model):
option_id = models.BigIntegerField(primary_key=True)
option_name = models.CharField(max_length=192, unique=True)
option_value = models.TextField()
autoload = models.CharField(max_length=60)

class Meta:
db_table = u'wp_options'

class WpUsermeta(models.Model):
umeta_id = models.BigIntegerField(primary_key=True)
user_id = models.BigIntegerField()
meta_key = models.CharField(max_length=765, blank=True)
meta_value = models.TextField(blank=True)

class Meta:
db_table = u'wp_usermeta'

class WpUsers(models.Model):
# Field name made lowercase.
id = models.BigIntegerField(primary_key=True, db_column='ID')

login = models.CharField(max_length=180, db_column='user_login')
password = models.CharField(max_length=192, db_column='user_pass')
nicename = models.CharField(max_length=150, db_column='user_nicename')
email = models.CharField(max_length=300, db_column='user_email')
url = models.CharField(max_length=300, db_column='user_url')
user_registered = models.DateTimeField(db_column='user_registered')
user_activation_key = models.CharField(max_length=180,
db_column='user_activation_key')
user_status = models.IntegerField(db_column='user_status')
display_name = models.CharField(max_length=750, db_column='display_name')

class Meta:
db_table = u'wp_users'

def __str__(self):
return str(self.login)

@property
def roles(self):
""" Returns a list of all roles for the user. """
php_serialized_roles = WpUsermeta.objects.using('wordpress').get(
user_id=self.id, meta_key='wp_capabilities').meta_value
roles = phpserialize.loads(php_serialized_roles)
return [role for role, enabled in roles.iteritems() if enabled]

@property
def capabilities(self):
capabilities = []
roles_data = phpserialize.loads(
WpOptions.objects.using('wordpress')\
.get(option_name='wp_user_roles').option_value)
for role in self.roles:
role_capabilities = roles_data.get(role).get('capabilities')
for capability, enabled in role_capabilities.iteritems():
if enabled:
capabilities.append(capability)
return set(capabilities)
29 changes: 29 additions & 0 deletions django_wordpress_auth/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Deprecated, can be removed
import os

APP_NAME = os.path.basename(os.path.abspath(os.path.join(__file__, os.path.pardir)))


class WordpressRouter(object):

def db_for_read(self, model, **hints):
"Point all operations on wordpress models to 'wordpress'"
if model._meta.app_label == APP_NAME:
return 'wordpress'
return None

def db_for_write(self, model, **hints):
"Point all operations on wordpress models to 'wordpress'"
if model._meta.app_label == APP_NAME:
return 'wordpress'
return None

def allow_relation(self, obj1, obj2, **hints):
"Allow any relation if a model in wordpress is involved"
if obj1._meta.app_label == APP_NAME or obj2._meta.app_label == APP_NAME:
return True
return None

def allow_syncdb(self, db, model):
"We don't create the wordpress tables via Django."
return model._meta.app_label != APP_NAME
28 changes: 28 additions & 0 deletions django_wordpress_auth/routers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import os

APP_NAME = os.path.basename(os.path.abspath(os.path.join(__file__, os.path.pardir)))


class WordpressRouter(object):

def db_for_read(self, model, **hints):
"Point all operations on wordpress models to 'wordpress'"
if model._meta.app_label == APP_NAME:
return 'wordpress'
return None

def db_for_write(self, model, **hints):
"Point all operations on wordpress models to 'wordpress'"
if model._meta.app_label == APP_NAME:
return 'wordpress'
return None

def allow_relation(self, obj1, obj2, **hints):
"Allow any relation if a model in wordpress is involved"
if obj1._meta.app_label == APP_NAME or obj2._meta.app_label == APP_NAME:
return True
return None

def allow_syncdb(self, db, model):
"We don't create the wordpress tables via Django."
return model._meta.app_label != APP_NAME
20 changes: 20 additions & 0 deletions django_wordpress_auth/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from django.http import HttpResponse

from . import get_wordpress_user
from decorators import wordpress_login_required, wordpress_requires_role, \
wordpress_requires_capability


@wordpress_login_required
def show_session(request):
return HttpResponse(request.wordpress_user.login)


@wordpress_requires_role('lima_member')
def test_roles(request):
return HttpResponse('Success')


@wordpress_requires_capability('view_cls_records')
def test_capabilities(request):
return HttpResponse('Success')
103 changes: 103 additions & 0 deletions readme.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
=====================
Django Wordpress Auth
=====================

Introduction
============

Allows for access in Django to a Wordpress installation for checking for
things like login status and roles / capabilities.

Requirements
============

Python Dependencies :

* `phpserialize`_

Wordpress Dependencies :

* `root Cookie`_
* `Members`_

.. _`phpserialize`: http://pypi.python.org/pypi/phpserialize
.. _`root Cookie`: http://wordpress.org/extend/plugins/root-cookie/
.. _`Members`: http://wordpress.org/extend/plugins/members/

Installation
============

Add your wordpress's auth keys and salts (found in wp-config.php).

.. sourcecode:: python

LOGGED_IN_KEY = "rs&^D%jPdu=vk|VVDsdfsdgsdgsdg9sd87f98s7h[Xm$3gT/@1xdasd"
LOGGED_IN_SALT = "3]x^n{d8=su23902iu09jdc09asjd09asjd09jasdV-Lv-OydAQ%?~"

Add your wordpress database.

.. sourcecode:: python

DATABASES = {
'default': {
... # default django DB
},
'wordpress': { # must be named 'wordpress'
'ENGINE': 'django.db.backends.mysql',
'NAME': 'wordpress',
'USER': 'XXX',
'PASSWORD': 'XXX',
'HOST': '',
'PORT': '',
}
}

Add the middleware. Make sure it's placed somewhere after the session
middleware.

.. sourcecode:: python

MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
# ...
'django_wordpress_auth.middleware.WordpressAuthMiddleware',
)

Finally, add to installed apps.

.. sourcecode:: python

INSTALLED_APPS = (
# ...
'django_wordpress_auth',
)

Usage
=====

To restrict a view to a certain role, simply wrap the view in the
``wordpress_requires_role`` decorator.

.. sourcecode:: python

from django_wordpress_auth.decorators import wordpress_requires_role

@wordpress_requires_role('my_role')
def my_view():
pass

You can restrict a view to a capability as well.

.. sourcecode:: python

from django_wordpress_auth.decorators import wordpress_requires_capability

@wordpress_requires_capability('my_capability')
def my_view():
pass

Finally, the middleware provides access to the wordpress user via
``request.wordpress_user``.

See ``models.py`` for full reference. Some of the redundant naming conventions
in the wordpress database have been made simpler as well.

0 comments on commit fabbec9

Please sign in to comment.