-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
django admin two factor auth is ready
- Loading branch information
1 parent
29663e3
commit ad55072
Showing
46 changed files
with
1,313 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -127,3 +127,4 @@ dmypy.json | |
|
||
# Pyre type checker | ||
.pyre/ | ||
.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
include LICENSE | ||
include README.rst | ||
recursive-include admin_two_factor/static * | ||
recursive-include admin_two_factor/templates * | ||
recursive-include docs * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,88 @@ | ||
# django-admin-two-factor-auth | ||
Two factor authentication for Django admin | ||
# Django Admin Two Factor Authentication | ||
|
||
**Django Admin Two-Factor Authentication**, allows you to login django admin with google authenticator. | ||
|
||
<br> | ||
|
||
## Why Django Log Reader? | ||
|
||
- Using google authenticator to login your Django admin. | ||
- Used jquery confirm dialog to get code. | ||
- Simple interface | ||
- Easy integration | ||
|
||
<br /> | ||
|
||
[comment]: <> () | ||
|
||
[comment]: <> (<br />) | ||
|
||
## How to use it | ||
|
||
* Download and install last version of **Django Admin Two-Factor Authentication**: | ||
|
||
```bash | ||
$ pip install git+https://github.com/imankarimi/django-admin-two-factor-auth.git | ||
$ # or | ||
$ easy_install git+https://github.com/imankarimi/django-admin-two-factor-auth.git | ||
``` | ||
|
||
* Add 'admin_two_factor' application to the INSTALLED_APPS setting of your Django project `settings.py` file (note it should be before 'django.contrib.admin'): | ||
|
||
```python | ||
INSTALLED_APPS = ( | ||
'admin_two_factor.apps.TwoStepVerificationConfig', | ||
'django.contrib.admin', | ||
# ... | ||
) | ||
``` | ||
|
||
* Migrate `admin_two_factor`: | ||
|
||
```bash | ||
$ python manage.py migrate admin_two_factor | ||
$ # or | ||
$ python manage.py syncdb | ||
``` | ||
|
||
* Add `ADMIN_TWO_FACTOR_NAME` in your `settings.py`. This value will be displayed in [Google Authenticator](https://support.google.com/accounts/answer/1066447?hl=en). | ||
|
||
```python | ||
ADMIN_TWO_FACTOR_NAME = 'PROJECT_NAME' | ||
``` | ||
|
||
* Include the **Admin Two Factor** URL config in `PROJECT_CORE/urls.py`: | ||
|
||
```python | ||
urlpatterns = [ | ||
path('admin/', admin.site.urls), | ||
path('two_factor/', include(('admin_two_factor.urls', 'admin_two_factor'), namespace='two_factor')), | ||
# ... | ||
] | ||
``` | ||
|
||
* Collect static if you are in production environment: | ||
|
||
```bash | ||
$ python manage.py collectstatic | ||
``` | ||
|
||
* Clear your browser cache | ||
|
||
<br /> | ||
|
||
## Start the app | ||
|
||
```bash | ||
$ # Set up the database | ||
$ python manage.py makemigrations | ||
$ python manage.py migrate | ||
$ | ||
$ # Create the superuser | ||
$ python manage.py createsuperuser | ||
$ | ||
$ # Start the application (development mode) | ||
$ python manage.py runserver # default port 8000 | ||
``` | ||
|
||
* Access the `admin` section in the browser: `http://127.0.0.1:8000/` |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from django.contrib import admin, messages | ||
from django.shortcuts import redirect | ||
from django.template.loader import render_to_string | ||
from django.utils.translation import gettext as _ | ||
from admin_two_factor.models import TwoFactorVerification | ||
from admin_two_factor.utils import set_expire | ||
|
||
|
||
@admin.register(TwoFactorVerification) | ||
class TwoStepVerificationAdmin(admin.ModelAdmin): | ||
list_display = ['user', 'is_active', 'created_time'] | ||
raw_id_fields = ['user'] | ||
list_filter = ['is_active', 'created_time'] | ||
fieldsets = (("", {'fields': ('user', 'code', 'is_active', 'qrcode'), }),) | ||
readonly_fields = ['qrcode'] | ||
|
||
def qrcode(self, obj): | ||
secret_key, qrcode = obj.get_qrcode | ||
if qrcode: | ||
return render_to_string('two_step_verification/admin/qrcode.html', {'qrcode': qrcode}) | ||
|
||
qrcode.short_description = _('Two Step QR Code') | ||
|
||
def get_fieldsets(self, request, obj=None): | ||
fieldsets = self.fieldsets | ||
if not obj: | ||
fieldsets = (("", {'fields': ('user',), }),) | ||
elif obj and obj.secret: | ||
fieldsets = (("", {'fields': ('user', 'code', 'is_active'), }),) | ||
return fieldsets | ||
|
||
def response_add(self, request, obj, post_url_continue=None): | ||
self.message_user(request, _('user added successfully'), level=messages.SUCCESS) | ||
return redirect('admin:admin_two_factor_twofactorverification_change', obj.id) | ||
|
||
def response_change(self, request, obj): | ||
request.session['two_step_%s' % request.user.id] = {'expire': set_expire().get('time')} | ||
return super(TwoStepVerificationAdmin, self).response_change(request, obj) | ||
|
||
def changeform_view(self, request, object_id=None, form_url='', extra_context=None): | ||
extra_context = extra_context or {} | ||
if not self.get_object(request, object_id): | ||
extra_context['show_save_and_add_another'] = False | ||
return super(TwoStepVerificationAdmin, self).changeform_view(request, object_id, form_url, extra_context) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class TwoStepVerificationConfig(AppConfig): | ||
name = 'admin_two_factor' | ||
verbose_name = 'two factor' |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from django.contrib.auth import logout | ||
|
||
from admin_two_factor.utils import is_expired | ||
|
||
|
||
def verification(request): | ||
user = request.user | ||
can_redirect = False | ||
if user.is_authenticated and user.is_staff and hasattr(user, 'two_step') and user.two_step.is_active: | ||
key = 'two_step_%s' % user.id | ||
user_session = request.session[key] if key in request.session else None | ||
if not user_session or is_expired(user_session['expire']): | ||
if user.id: | ||
logout(request) | ||
can_redirect = True | ||
return {'can_redirect': can_redirect} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Generated by Django 3.1.3 on 2020-11-09 14:35 | ||
|
||
from django.conf import settings | ||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='TwoFactorVerification', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('secret', models.CharField(blank=True, editable=False, max_length=20, null=True, unique=True, verbose_name='secret key')), | ||
('code', models.CharField(blank=True, help_text='You must enter the code here to active/deactivate two step verification.', max_length=8, null=True, verbose_name='code')), | ||
('is_active', models.BooleanField(default=False, verbose_name='is active?')), | ||
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='created time')), | ||
('updated_time', models.DateTimeField(auto_now=True, verbose_name='updated time')), | ||
('user', models.OneToOneField(on_delete=django.db.models.deletion.DO_NOTHING, related_name='two_step', to=settings.AUTH_USER_MODEL)), | ||
], | ||
options={ | ||
'verbose_name': 'two factor verification', | ||
'verbose_name_plural': 'two factor verifications', | ||
}, | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import base64 | ||
import os | ||
from io import BytesIO | ||
|
||
import pyotp | ||
import qrcode | ||
from admin_two_factor import settings | ||
from django.contrib.auth.models import User | ||
from django.core.cache import cache | ||
from django.core.exceptions import ValidationError | ||
from django.db import models | ||
from django.utils.translation import gettext as _ | ||
|
||
|
||
class TwoFactorVerification(models.Model): | ||
user = models.OneToOneField(User, on_delete=models.DO_NOTHING, related_name='two_step') | ||
secret = models.CharField(_('secret key'), max_length=20, null=True, blank=True, unique=True, editable=False) | ||
code = models.CharField(_('code'), max_length=8, null=True, blank=True, | ||
help_text=_('You must enter the code here to active/deactivate two step verification.')) | ||
is_active = models.BooleanField(_('is active?'), default=False) | ||
|
||
created_time = models.DateTimeField(_('created time'), auto_now_add=True) | ||
updated_time = models.DateTimeField(_('updated time'), auto_now=True) | ||
|
||
class Meta: | ||
verbose_name = _('two factor verification') | ||
verbose_name_plural = _('two factor verifications') | ||
|
||
def clean(self): | ||
if self.is_active and not self.secret and not self.code: | ||
raise ValidationError("The code field is required.") | ||
if not self.is_active and self.secret and not self.code: | ||
raise ValidationError("The code field is required.") | ||
|
||
if self.code: | ||
if self.is_active and not self.secret: | ||
secret = cache.get('user_secret_key_%s' % self.user.id) | ||
|
||
is_verify = self.is_verify(code=self.code, secret=secret) | ||
if not is_verify: | ||
raise ValidationError("The code is wrong. please try again.") | ||
|
||
self.secret = secret | ||
|
||
elif not self.is_active and self.secret: | ||
is_verify = self.is_verify(code=self.code) | ||
if not is_verify: | ||
raise ValidationError("The code is wrong. please try again.") | ||
self.secret = None | ||
|
||
self.code = None | ||
|
||
@property | ||
def get_qrcode(self): | ||
if self.secret or not self.user: | ||
return self.secret, None | ||
|
||
secret_key = base64.b32encode(os.urandom(10)).decode() | ||
username = self.user.get_full_name() if self.user.get_full_name() else self.user.username | ||
query = pyotp.totp.TOTP(secret_key).provisioning_uri(username, | ||
issuer_name=settings.ADMIN_TWO_FACTOR_NAME) | ||
qr_img = qrcode.make(query) | ||
buffered = BytesIO() | ||
qr_img.save(buffered, format="JPEG") | ||
link = base64.b64encode(buffered.getvalue()).decode('UTF-8') | ||
|
||
cache.set('user_secret_key_%s' % self.user_id, secret_key, 300) | ||
|
||
return secret_key, link | ||
|
||
def is_verify(self, code, secret=None): | ||
secret = secret if secret else self.secret | ||
if secret: | ||
totp = pyotp.TOTP(secret) | ||
if code == totp.now(): | ||
return True | ||
return False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from django.conf import settings | ||
|
||
# Two factor name | ||
ADMIN_TWO_FACTOR_NAME = getattr(settings, 'ADMIN_TWO_FACTOR_NAME', None) | ||
|
||
# two factor session expire time (second) | ||
SESSION_COOKIE_AGE = getattr(settings, 'SESSION_COOKIE_AGE', 300) | ||
|
||
# Two factor context processors | ||
settings.TEMPLATES[0]['OPTIONS']['context_processors'].append( | ||
'admin_two_factor.context_processors.two_factor.verification' | ||
) |
9 changes: 9 additions & 0 deletions
9
admin_two_factor/static/two_step_assets/css/jquery-confirm.min.css
Large diffs are not rendered by default.
Oops, something went wrong.
4 changes: 4 additions & 0 deletions
4
admin_two_factor/static/two_step_assets/jquery-cookie/.gitignore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
node_modules | ||
build | ||
.sizecache.json | ||
*.log |
17 changes: 17 additions & 0 deletions
17
admin_two_factor/static/two_step_assets/jquery-cookie/.jshintrc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"boss": true, | ||
"browser": true, | ||
"curly": true, | ||
"eqeqeq": true, | ||
"eqnull": true, | ||
"expr": true, | ||
"evil": true, | ||
"newcap": true, | ||
"noarg": true, | ||
"undef": true, | ||
"globals": { | ||
"define": true, | ||
"jQuery": true, | ||
"require": true | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
admin_two_factor/static/two_step_assets/jquery-cookie/.tm_properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
softTabs = false | ||
tabSize = 2 | ||
|
||
[ text.plain ] | ||
softWrap = true | ||
wrapColumn = "Use Window Frame" | ||
softTabs = true | ||
tabSize = 4 | ||
|
||
[ "*.md" ] | ||
fileType = "text.plain" |
10 changes: 10 additions & 0 deletions
10
admin_two_factor/static/two_step_assets/jquery-cookie/.travis.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
language: node_js | ||
node_js: | ||
- 0.8 | ||
before_script: | ||
- npm install -g grunt-cli | ||
script: grunt ci --verbose | ||
env: | ||
global: | ||
- secure: HRae0kyIDDuhonvMi2SfEl1WJb4K/wX8WmzT9YkxFbmWwLjiOMkmqyuEyi76DbTC1cb9o7WwGVgbP1DhSm6n6m0Lz+PSzpprBN4QZuJc56jcc+tBA6gM81hyUufaTT0yUWz112Bu06kWIAs44w5PtG0FYZR0CuIN8fQvZi8fXCQ= | ||
- secure: c+M5ECIfxDcVrr+ZlqgpGjv8kVM/hxiz3ACMCn4ZkDiaeq4Rw0wWIGZYL6aV5fhsoHgzEQ/XQPca8xKs3Umr7R3b6Vr3AEyFnW+LP67K/1Qbz4Pi3PvhDH/h4rvK7fOoTqTDCVVDEH3v4pefsz2VaKemG4iBKxrcof5aR4Rjopk= |
Oops, something went wrong.