gpgAuth Forums
May 19, 2012, 04:39:50 pm *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
News:
 
   Home   Help Search Login Register  
Pages: [1]
  Print  
Author Topic: Django Implementation - (Django/Apache/Linux)  (Read 1409 times)
kylehuff
Administrator
Newbie
*****
Posts: 12

432856929 gpgauth
View Profile Email
« on: July 11, 2009, 09:02:25 am »

This was made for version 1.2, which is now obsolete; we are on v1.3.0 - I will replace this ASAP with an updated version.

**** THIS EXAMPLE IS NO LONGER CURRENT ****

This method uses the django.contrib.auth authenticate and login functions, but this can be changed to a custom authentication backend. I am working on a django authentication backend for gpgAuth, it is not finished yet.

Requirements:
    (These requirements and this Django implementation is based Linux and Apache, but the Django code should be the same for any platform Django supports)
    Apache
    Python
    Django
    GnuPG
    python module gnupginterface

Setup:

If you are using the django.contrib.auth module for your user objects, it will need to be extended to provide a place to store the users public key fingerprint and the value of the authentication token.

To extend the user object, add this line to your settings.py file for your project:
Code:
AUTH_PROFILE_MODULE = 'userprofile.UserProfile'

Then create the app:
Code:
python manage.py startapp userprofile

This will create the blank views.py and models.py. Edit the models.py file to include:
Code:
from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
fingerprint = models.TextField(null=True,blank=True)
gpgauth_token = models.TextField(null=True,blank=True)

class Meta:
verbose_name = ('User Profile')
verbose_name_plural = ('User Profiles')

def __str__( self ):
return "%s - Profile Object" % ( self.user.username )

If you want these objects to be accessible within the built-in django admin site, create an admin.py file in the 'userprofile' app directory and add the following lines:
Code:
from example.userprofile.models import UserProfile
from django.contrib import admin

admin.site.register(UserProfile)


Make sure the following is part of your INSTALLED_APPS context in your settings.py file:
Code:
INSTALLED_APPS = (
    'django.contrib.auth',
),

Add these lines to your settings.py file (or change them if they already exist) - this will override where users are pointed when told to login using the @login_require decorator):
Code:
LOGIN_URL = '/login'
LOGOUT_URL = '/logout'

Now edit or create the URLs for the project by adding these lines to the urls.py file (the admin line is optional, this just creates the URLs for the built-in django admin site.):
Code:
urlpatterns = patterns('',
    (r'^admin/(.*)', admin.site.root),
    (r'^login/$', 'example.views.login_view'),
    (r'^login/(?P<stage>\d+)', 'example.views.login_view'),
    (r'^logout/$', 'example.views.logout_view'),
)

Now, within the views.py file of your main project, add the following views:
Code:
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate, login, logout
from example.gpgauth import gpgauthBackend
from django.contrib.auth.models import User
from cgi import parse_qs


def login_view(request, stage=u'1'):
if stage == u'1':
try:
user = request.user
if user.is_authenticated:
logout(request)
except:
pass
return render_to_response('login.html', { 'stage1' : True }, context_instance=RequestContext(request))
if stage == u'2':
try:
   user = User.objects.get(username=request.POST['username'])
except User.DoesNotExist:
user = None
if request.POST['username'] and user:
if request.POST['password']:
''' Perform legacy authentication '''
user = None
user = authenticate(username=request.POST['username'], password=request.POST['password'])
if user is not None:
if user.is_active:
''' User is authenticated, call the login function '''
login(request, user)
return render_to_response('login.html', { 'stage3' : True, 'auth_method':'password', 'error' : None }, context_instance=RequestContext(request))
else:
''' Account is not active '''
return render_to_response('login.html', { 'stage1' : True, 'auth_method':'password', 'error' : True, 'error_message' : 'account disabled' }, context_instance=RequestContext(request))
else:
''' Username or password were incorrect '''
return render_to_response('login.html', { 'stage1' : True, 'auth_method':'password', 'error' :  True, 'error_message' : 'invalid login' }, context_instance=RequestContext(request))
elif request.POST.has_key('gpg_auth:server_token') and len(request.POST['gpg_auth:server_token']) and user.userprofile_set.values() and user.userprofile_set.values()[0].has_key('fingerprint'):
''' Perform gpg authentication '''
server_token = parse_qs( request.raw_post_data )['gpg_auth:server_token'][0]
result = gpgauthBackend().authenticate( username=request.POST['username'], password=request.POST['password'], server_token=server_token )
request.session['auth_user'] = request.POST['username']
return render_to_response('login.html', { 'stage2' : True, 'decrypted_server_token' : result['decrypted_server_token'], 'encrypted_user_token' : result['encrypted_user_token'], 'auth_user':result['user'], 'error' : result['error'], 'error_message' : result['error_message'] }, context_instance=RequestContext(request))
else:
''' An error has occurred '''
if not user.userprofile_set.values() or not user.userprofile_set.values()[0].has_key('fingerprint'):
error = "This account is not configured for gpgAuth login"
if not request.POST.has_key('gpg_auth:server_token') or not len(request.POST['gpg_auth:server_token']):
error = "No token provided by the client."
return render_to_response('login.html', { 'stage1' : True, 'error' : True, 'error_message' : error }, context_instance=RequestContext(request))
else:
''' Invalid username or password '''
return render_to_response('login.html', { 'stage1' : True, 'error' : True, 'error_message' : 'Invalid username or password' }, context_instance=RequestContext(request))

if stage == u'3':
error = None
error_message = None
username = request.session.get( "auth_user" )
try:
user = User.objects.get(username=username)
if user.userprofile_set.values() and user.userprofile_set.values()[0].has_key('gpgauth_token'):
user_token = user.get_profile().gpgauth_token
if user_token == request.POST['user_response_token']:
''' the tokens match, lets login.. '''
user.backend = 'example.gpgauth.gpgauthBackend'
user.get_profile().gpgauth_token = None
user.get_profile().save()
login(request, user)
else:
user_token = "No token provided"
error = True
error_message = user_token
except User.DoesNotExist:
user = None
error = True
error_message = "invalid user"
return render_to_response('login.html', { 'stage3' : True, 'auth_method':'gpgAuth', 'user_token' : user_token, 'user_response_token' : request.POST['user_response_token'], 'error' : error, 'error_message' : error_message }, context_instance=RequestContext(request))

def logout_view(request):
logout(request)
return render_to_response( 'login.html', { 'stage1' : True }, context_instance=RequestContext(request) )


And finally, create a gpgauth.py file in the root of your project and add the following code to the file (NOTE: the string should be random data consisting of a-Z and 0-9 (alphanumeric) of any length. The example crypto_token is static, it never changes, it is only an example....)
Code:
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.auth import authenticate as legacy_authenticate
import GnuPGInterface

class gpgauthBackend:
"""
Authenticate using GnuPG/PGP via the gpgauth protocol (see http://www.gpgauth.org).
Django gpgAuth integration module v0.1
go to http://www.gpgauth.org for the latest version.
"""
def authenticate(self, username=None, password=None, server_token=None, user_token=None):
gnupg = self.MyGnuPG()
global gnupg
auth_user = None
decrypted_server_token = None
encrypted_user_token = None
user = User.objects.get(username=username)
if user.userprofile_set.values() and user.userprofile_set.values()[0].has_key('fingerprint') and user.get_profile().fingerprint and server_token:
user_fp = user.get_profile().fingerprint
user.get_profile().gpgauth_token = None
user.get_profile().save()
decrypted_server_token = gnupg.decrypt_string( server_token )
crypto_token = "qkEbiHSLzHfWHNvnKgPWMPdDrKxbczfjyFcGTtZCDKq"
user_token = "gpgauthv1.2.1|%s|%s|gpgauthv1.2.1" % ( len(crypto_token), crypto_token )
encrypted_user_token = gnupg.encrypt_and_sign_string( user_token, [user_fp.replace(" ", "" )] )
user.get_profile().gpgauth_token = user_token
user.get_profile().save()
auth_method = "gpgAuth"
return { 'auth_method' : auth_method, 'user' : user, 'decrypted_server_token' : decrypted_server_token, 'encrypted_user_token' : encrypted_user_token, 'error' : None, 'error_message' : None }
else:
return { 'auth_method' : 'gpgAuth', 'user' : user, 'error' :  True, 'error_message' : 'no authentication key on file for user', 'encrypted_user_token' : encrypted_user_token, 'decrypted_server_token' : decrypted_server_token }


def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None

class MyGnuPG(GnuPGInterface.GnuPG):
def __init__(self):
gnupg = GnuPGInterface.GnuPG.__init__(self)
self.setup_my_options()

def setup_my_options(self):
self.options.armor = 1
self.options.meta_interactive = 0
self.options.extra_args.append('--no-secmem-warning')
self.options.default_key = 'example.com'
self.options.homedir = '/var/www/example.com/.gnupg'

def decrypt_string( self, string ):
gnupg.passphrase = ''
proc = gnupg.run( ['--decrypt', '-a'], create_fhs=['stdin', 'stdout','stderr'] )
proc.handles['stdin'].write(str(string))
proc.handles['stdin'].close()
output = proc.handles['stdout'].read()
proc.handles['stdout'].close()
error = proc.handles['stderr'].read()
error = proc.handles['stderr'].close()
proc.wait()
return output

def encrypt_and_sign_string(self, string, recipients):
gnupg.options.recipients = recipients   # a list!
proc = gnupg.run(['--sign', '--encrypt'], create_fhs=['stdin', 'stdout'])
proc.handles['stdin'].write(string)
proc.handles['stdin'].close()
output = proc.handles['stdout'].read()
proc.handles['stdout'].close()
proc.wait()
return output

def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None


Now, once you have added the users fingerprint to the database, and imported the users public_key into the GnuPG Keystore, that user should be able to login using gpgAuth, or the legacy password authentication, to disable the password method, just provide call the set_unusable_password on the given user object, and then call user_object.save(), then only gpgAuth login will work for the given user.

« Last Edit: December 27, 2010, 12:54:34 pm by kylehuff » Logged
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.9 | SMF © 2006-2009, Simple Machines LLC Valid XHTML 1.0! Valid CSS!