Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add a simple login honypot

  • Loading branch information...
commit e6acce7cb7a4b9fe2dd6618bfb8640ae3b23769b 1 parent 6dbad0e
@jedie authored
View
37 pylucid_project/pylucid_plugins/auth/admin.py
@@ -0,0 +1,37 @@
+# coding: utf-8
+
+from django.contrib import admin
+
+from pylucid_project.pylucid_plugins.auth.models import HonypotAuth,\
+ HonypotUsername, HonypotPassword, HonypotIP
+
+
+class HonypotAuthAdmin(admin.ModelAdmin):
+ list_display = ("id", "username", "password", "ip_address", "count", "lastupdatetime")
+ list_display_links = ("username", "password", "ip_address")
+ list_filter = ("ip_address",)
+ date_hierarchy = 'lastupdatetime'
+ search_fields = ("username", "password")
+admin.site.register(HonypotAuth, HonypotAuthAdmin)
+
+
+class HonypotUsernameAdmin(admin.ModelAdmin):
+ list_display = ("id", "username", "count")
+ list_display_links = ("username",)
+ search_fields = ("username",)
+admin.site.register(HonypotUsername, HonypotUsernameAdmin)
+
+
+class HonypotPasswordAdmin(admin.ModelAdmin):
+ list_display = ("id", "password", "count")
+ list_display_links = ("password",)
+ search_fields = ("password",)
+admin.site.register(HonypotPassword, HonypotPasswordAdmin)
+
+
+class HonypotIPAdmin(admin.ModelAdmin):
+ list_display = ("id", "ip_address", "count")
+ list_display_links = ("ip_address",)
+ search_fields = ("ip_address",)
+admin.site.register(HonypotIP, HonypotIPAdmin)
+
View
11 pylucid_project/pylucid_plugins/auth/forms.py
@@ -5,13 +5,24 @@
from django.contrib import auth
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
+from django.forms.forms import NON_FIELD_ERRORS
from pylucid_project.utils import crypt
from pylucid_project.apps.pylucid.models import UserProfile
+from django.forms.util import ErrorDict
+
class WrongUserError(Exception):
pass
+
+class HoneypotForm(forms.Form):
+ username = forms.CharField(max_length=30, label=_('username'))
+ password = forms.CharField(max_length=128, label=_('password'),
+ widget=forms.PasswordInput
+ )
+
+
class UsernameForm(forms.Form):
username = forms.CharField(max_length=_('30'), label=_('Username'),
help_text=_('Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores).')
View
126 pylucid_project/pylucid_plugins/auth/models.py
@@ -0,0 +1,126 @@
+# coding: utf-8
+
+
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+
+try:
+ from django_tools.models import UpdateTimeBaseModel
+except ImportError:
+ # New in django-tools v0.24.2
+ # TODO: Remove this in next release:
+ try:
+ from django.utils.timezone import now
+ except ImportError:
+ from datetime import datetime
+ now = datetime.now
+
+ class UpdateTimeBaseModel(models.Model):
+ createtime = models.DateTimeField(default=now, editable=False, help_text="Create time")
+ lastupdatetime = models.DateTimeField(default=now, editable=False, help_text="Time of the last change.")
+ def save(self, *args, **kwargs):
+ self.lastupdatetime = now()
+ return super(UpdateTimeBaseModel, self).save(*args, **kwargs)
+ class Meta:
+ abstract = True
+
+
+class CountManager(models.Manager):
+ def __init__(self, attr_name, *args, **kwargs):
+ self.attr_name = attr_name
+ super(CountManager, self).__init__(*args, **kwargs)
+
+ def increase_or_add(self, value):
+ kwargs = {
+ "%s__exact" % self.attr_name: value,
+ "defaults": {self.attr_name: value}
+ }
+ obj, created = self.get_or_create(**kwargs)
+ if not created:
+ obj.count += 1
+ obj.save()
+ return obj
+
+
+class HonypotUsername(models.Model):
+ username = models.CharField(db_index=True, max_length=30)
+ count = models.PositiveIntegerField(default=1,
+ help_text=_("Number of use of this username.")
+ )
+ objects = CountManager("username")
+ def __unicode__(self):
+ return u"%s (count: %i)" % (self.username, self.count)
+ class Meta:
+ ordering = ('-count',)
+
+
+class HonypotPassword(models.Model):
+ password = models.CharField(db_index=True, max_length=128)
+ count = models.PositiveIntegerField(default=1,
+ help_text=_("Number of use of this password.")
+ )
+ objects = CountManager("password")
+ def __unicode__(self):
+ return u"%s (count: %i)" % (self.password, self.count)
+ class Meta:
+ ordering = ('-count',)
+
+
+class HonypotIP(models.Model):
+ ip_address = models.IPAddressField(db_index=True)
+ count = models.PositiveIntegerField(default=1,
+ help_text=_("Number of logins from this remote IP address.")
+ )
+ objects = CountManager("ip_address")
+ def __unicode__(self):
+ return u"%s (count: %i)" % (self.ip_address, self.count)
+ class Meta:
+ ordering = ('-count',)
+
+
+class HonypotAuthManager(models.Manager):
+ def add(self, request, username, password):
+ ip_address = request.META["REMOTE_ADDR"]
+ ip_address_obj = HonypotIP.objects.increase_or_add(ip_address)
+ username_obj = HonypotUsername.objects.increase_or_add(username)
+ password_obj = HonypotPassword.objects.increase_or_add(password)
+
+ obj, created = self.get_or_create(
+ username__exact=username_obj,
+ password__exact=password_obj,
+ ip_address__exact=ip_address_obj,
+ defaults={
+ "username":username_obj,
+ "password":password_obj,
+ "ip_address":ip_address_obj,
+ }
+ )
+ if not created:
+ obj.count += 1
+ obj.save()
+ return obj
+
+
+class HonypotAuth(UpdateTimeBaseModel):
+ """
+ inherited attributes from UpdateTimeBaseModel:
+ createtime -> datetime of creation
+ lastupdatetime -> datetime of the last change
+ """
+ objects = HonypotAuthManager()
+
+ username = models.ForeignKey(HonypotUsername)
+ password = models.ForeignKey(HonypotPassword)
+ ip_address = models.ForeignKey(HonypotIP)
+ count = models.PositiveIntegerField(default=1,
+ help_text=_("Number of usage this username/password from the same remote IP address.")
+ )
+
+ def __unicode__(self):
+ return u"honypot login from %s [%s/%s] (count: %i)" % (
+ self.ip_address, self.username, self.password, self.count
+ )
+
+ class Meta:
+ ordering = ('-lastupdatetime',)
View
5 pylucid_project/pylucid_plugins/auth/preference_forms.py
@@ -16,5 +16,10 @@ class AuthPreferencesForm(DBPreferencesBaseForm):
initial=15, min_value=1, max_value=600
)
+ use_honypot = forms.BooleanField(
+ help_text=_("Enable login honypot? (A PluginPage must be created!)"),
+ initial = False, required=False
+ )
+
class Meta:
app_label = 'auth'
View
11 pylucid_project/pylucid_plugins/auth/templates/auth/login_honeypot.html
@@ -0,0 +1,11 @@
+{% extends template_name %}
+
+
+{% block content %}
+<form method="post" action="{{ form_url }}" class="pylucid_form">
+ <fieldset><h2>Login Form</h2>
+ {% include "admin/pylucid/includes/pylucid_formset.html" %}
+ <input type="submit" value="login" />
+ </fieldset>
+</form>
+{% endblock content %}
View
2  pylucid_project/pylucid_plugins/auth/templates/auth/login_link.html
@@ -1,5 +1,5 @@
{% extends "pylucid/css_anchor_span.html" %}
{% block plugin_content %}
-<a href="#top" id="login_link" rel="nofollow" onclick="return get_pylucid_ajax_view('{{ url }}');">{% trans 'Log in' %}</a>
+<a href="{{ honypot_url }}" id="login_link" rel="nofollow" onclick="return get_pylucid_ajax_view('{{ url }}');">{% trans 'Log in' %}</a>
{% endblock %}
View
11 pylucid_project/pylucid_plugins/auth/urls.py
@@ -0,0 +1,11 @@
+# coding: utf-8
+
+
+from django.conf.urls.defaults import patterns, url
+
+from pylucid_project.pylucid_plugins.auth import views
+
+
+urlpatterns = patterns('',
+ url(r'^', views.login_honeypot, name='Auth-login_honeypot'),
+)
View
57 pylucid_project/pylucid_plugins/auth/views.py
@@ -26,8 +26,14 @@
# auth own stuff
from forms import WrongUserError, UsernameForm, ShaLoginForm
from preference_forms import AuthPreferencesForm
-from django.views.decorators.csrf import requires_csrf_token, csrf_protect
+from django.views.decorators.csrf import requires_csrf_token, csrf_protect,\
+ csrf_exempt
from pylucid_project.apps.pylucid.decorators import check_request
+from pylucid_project.pylucid_plugins.auth.forms import HoneypotForm
+from pylucid_project.pylucid_plugins.auth.models import HonypotAuth
+from django.shortcuts import render_to_response
+from pylucid_project.apps.pylucid.models.pluginpage import PluginPage
+from django.core import urlresolvers
APP_LABEL = "pylucid_plugin.auth"
@@ -57,11 +63,43 @@ def _get_challenge(request):
return challenge
+@csrf_exempt
+def login_honeypot(request):
+ """
+ A login honypot.
+ """
+ faked_login_error = False
+ if request.method == 'POST':
+ form = HoneypotForm(request.POST)
+ if form.is_valid():
+ username = form.cleaned_data["username"]
+ password = form.cleaned_data["password"]
+ HonypotAuth.objects.add(request, username, password)
+ messages.error(request, _("username/password wrong."))
+ form = HoneypotForm(initial={"username": username})
+ faked_login_error = True
+ else:
+ form = HoneypotForm()
+ context = {
+ "form": form,
+ "form_url": request.path,
+ "page_robots": "noindex,nofollow",
+ }
+
+ response = render_to_response("auth/login_honeypot.html", context, context_instance=RequestContext(request))
+ if faked_login_error:
+ response.status_code = 401
+ return response
+
+
def lucidTag(request):
"""
Create login/logout link
example: {% lucidTag auth %}
"""
+ context = {
+ "honypot_url": "#top" # Don't use honypot
+ }
if request.user.is_authenticated():
template_name = "auth/logout_link.html"
if hasattr(request.PYLUCID, "pagetree"):
@@ -74,8 +112,21 @@ def lucidTag(request):
else:
template_name = "auth/login_link.html"
url = "?auth=login"
-
- return render_to_string(template_name, {"url": url}, context_instance=RequestContext(request))
+ pref_form = AuthPreferencesForm()
+ preferences = pref_form.get_preferences()
+ use_honypot = preferences["use_honypot"]
+ if use_honypot:
+ try: # Use the first PluginPage instance
+ honypot_url = PluginPage.objects.reverse("auth", 'Auth-login_honeypot')
+ except urlresolvers.NoReverseMatch, err:
+ if settings.RUN_WITH_DEV_SERVER:
+ print "*** Can't get 'Auth-login_honeypot' url: %s" % err
+ else:
+ context["honypot_url"] = honypot_url
+
+ context["url"] = url
+
+ return render_to_string(template_name, context, context_instance=RequestContext(request))
def _wrong_login(request, debug_msg, user=None):
Please sign in to comment.
Something went wrong with that request. Please try again.