Draft: Two factor auth #118
7 changed files with 40 additions and 38 deletions
|
|
@ -1,4 +1,4 @@
|
|||
from rest_framework.permissions import BasePermission
|
||||
from rest_framework.permissions import BasePermission, IsAuthenticated
|
||||
|
||||
|
||||
class IsOwner(BasePermission):
|
||||
|
|
@ -21,3 +21,9 @@ class IsPostOwner(BasePermission):
|
|||
return bool(is_category_user and is_rule_user)
|
||||
|
||||
return is_rule_user
|
||||
|
||||
|
||||
class TwoFactorAuthenticated(IsAuthenticated):
|
||||
def has_permission(self, request, view):
|
||||
is_authenticated = super().has_permission(request, view)
|
||||
return is_authenticated and request.user.is_verified()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.urls import include, path
|
||||
|
||||
from django_otp.decorators import otp_required
|
||||
from two_factor.views import (
|
||||
BackupTokensView,
|
||||
DisableView,
|
||||
|
|
@ -43,42 +43,43 @@ settings_patterns = [
|
|||
# Integrations
|
||||
path(
|
||||
"integrations/reddit/callback/",
|
||||
login_required(RedditTemplateView.as_view()),
|
||||
otp_required(RedditTemplateView.as_view()),
|
||||
name="reddit-template",
|
||||
),
|
||||
path(
|
||||
"integrations/reddit/refresh/",
|
||||
login_required(RedditTokenRedirectView.as_view()),
|
||||
otp_required(RedditTokenRedirectView.as_view()),
|
||||
name="reddit-refresh",
|
||||
),
|
||||
path(
|
||||
"integrations/reddit/revoke/",
|
||||
login_required(RedditRevokeRedirectView.as_view()),
|
||||
otp_required(RedditRevokeRedirectView.as_view()),
|
||||
name="reddit-revoke",
|
||||
),
|
||||
path(
|
||||
"integrations/twitter/auth/",
|
||||
login_required(TwitterAuthRedirectView.as_view()),
|
||||
otp_required(TwitterAuthRedirectView.as_view()),
|
||||
name="twitter-auth",
|
||||
),
|
||||
path(
|
||||
"integrations/twitter/callback/",
|
||||
login_required(TwitterTemplateView.as_view()),
|
||||
otp_required(TwitterTemplateView.as_view()),
|
||||
name="twitter-template",
|
||||
),
|
||||
path(
|
||||
"integrations/twitter/revoke/",
|
||||
login_required(TwitterRevokeRedirectView.as_view()),
|
||||
otp_required(TwitterRevokeRedirectView.as_view()),
|
||||
name="twitter-revoke",
|
||||
),
|
||||
path(
|
||||
"integrations/", login_required(IntegrationsView.as_view()), name="integrations"
|
||||
"integrations/", otp_required(IntegrationsView.as_view()), name="integrations"
|
||||
),
|
||||
# Misc
|
||||
path("favicon/", login_required(FaviconRedirectView.as_view()), name="favicon"),
|
||||
path("", login_required(SettingsView.as_view()), name="home"),
|
||||
path("favicon/", otp_required(FaviconRedirectView.as_view()), name="favicon"),
|
||||
path("", otp_required(SettingsView.as_view()), name="home"),
|
||||
]
|
||||
|
||||
# permissions are handled through the views itself
|
||||
two_factor = [
|
||||
path("accounts/setup/", SetupView.as_view(), name="setup"),
|
||||
path("accounts/qrcode/", QRGeneratorView.as_view(), name="qr"),
|
||||
|
|
@ -140,7 +141,7 @@ urlpatterns = [
|
|||
),
|
||||
path(
|
||||
"password-change/",
|
||||
login_required(PasswordChangeView.as_view()),
|
||||
otp_required(PasswordChangeView.as_view()),
|
||||
name="password-change",
|
||||
),
|
||||
# Settings
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ REST_FRAMEWORK = {
|
|||
"rest_framework.authentication.SessionAuthentication",
|
||||
),
|
||||
"DEFAULT_PERMISSION_CLASSES": (
|
||||
"rest_framework.permissions.IsAuthenticated",
|
||||
"newsreader.accounts.permissions.TwoFactorAuthenticated",
|
||||
"newsreader.accounts.permissions.IsOwner",
|
||||
),
|
||||
"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.urls import path
|
||||
|
||||
from django_otp.decorators import otp_required
|
||||
|
||||
from newsreader.news.collection.endpoints import (
|
||||
DetailRuleView,
|
||||
NestedRuleView,
|
||||
|
|
@ -29,48 +30,46 @@ endpoints = [
|
|||
|
||||
urlpatterns = [
|
||||
# Feeds
|
||||
path(
|
||||
"feeds/<int:pk>/", login_required(FeedUpdateView.as_view()), name="feed-update"
|
||||
),
|
||||
path("feeds/create/", login_required(FeedCreateView.as_view()), name="feed-create"),
|
||||
path("feeds/<int:pk>/", otp_required(FeedUpdateView.as_view()), name="feed-update"),
|
||||
path("feeds/create/", otp_required(FeedCreateView.as_view()), name="feed-create"),
|
||||
# Generic rules
|
||||
path("rules/", login_required(CollectionRuleListView.as_view()), name="rules"),
|
||||
path("rules/", otp_required(CollectionRuleListView.as_view()), name="rules"),
|
||||
path(
|
||||
"rules/delete/",
|
||||
login_required(CollectionRuleBulkDeleteView.as_view()),
|
||||
otp_required(CollectionRuleBulkDeleteView.as_view()),
|
||||
name="rules-delete",
|
||||
),
|
||||
path(
|
||||
"rules/enable/",
|
||||
login_required(CollectionRuleBulkEnableView.as_view()),
|
||||
otp_required(CollectionRuleBulkEnableView.as_view()),
|
||||
name="rules-enable",
|
||||
),
|
||||
path(
|
||||
"rules/disable/",
|
||||
login_required(CollectionRuleBulkDisableView.as_view()),
|
||||
otp_required(CollectionRuleBulkDisableView.as_view()),
|
||||
name="rules-disable",
|
||||
),
|
||||
path("rules/import/", login_required(OPMLImportView.as_view()), name="import"),
|
||||
path("rules/import/", otp_required(OPMLImportView.as_view()), name="import"),
|
||||
# Reddit
|
||||
path(
|
||||
"subreddits/create/",
|
||||
login_required(SubRedditCreateView.as_view()),
|
||||
otp_required(SubRedditCreateView.as_view()),
|
||||
name="subreddit-create",
|
||||
),
|
||||
path(
|
||||
"subreddits/<int:pk>/",
|
||||
login_required(SubRedditUpdateView.as_view()),
|
||||
otp_required(SubRedditUpdateView.as_view()),
|
||||
name="subreddit-update",
|
||||
),
|
||||
# Twitter
|
||||
path(
|
||||
"twitter/timelines/create/",
|
||||
login_required(TwitterTimelineCreateView.as_view()),
|
||||
otp_required(TwitterTimelineCreateView.as_view()),
|
||||
name="twitter-timeline-create",
|
||||
),
|
||||
path(
|
||||
"twitter/timelines/<int:pk>/",
|
||||
login_required(TwitterTimelineUpdateView.as_view()),
|
||||
otp_required(TwitterTimelineUpdateView.as_view()),
|
||||
name="twitter-timeline-update",
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -7,10 +7,8 @@ from rest_framework.generics import (
|
|||
RetrieveUpdateDestroyAPIView,
|
||||
get_object_or_404,
|
||||
)
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
|
||||
from newsreader.accounts.permissions import IsPostOwner
|
||||
from newsreader.core.pagination import CursorPagination
|
||||
from newsreader.news.collection.serializers import RuleSerializer
|
||||
from newsreader.news.core.filters import ReadFilter, SavedFilter
|
||||
|
|
@ -21,7 +19,6 @@ from newsreader.news.core.serializers import CategorySerializer, PostSerializer
|
|||
class ListPostView(ListAPIView):
|
||||
queryset = Post.objects.all()
|
||||
serializer_class = PostSerializer
|
||||
permission_classes = (IsAuthenticated, IsPostOwner)
|
||||
pagination_class = CursorPagination
|
||||
filter_backends = [ReadFilter, SavedFilter]
|
||||
|
||||
|
|
@ -29,7 +26,6 @@ class ListPostView(ListAPIView):
|
|||
class DetailPostView(RetrieveUpdateAPIView):
|
||||
queryset = Post.objects.all()
|
||||
serializer_class = PostSerializer
|
||||
permission_classes = (IsAuthenticated, IsPostOwner)
|
||||
|
||||
|
||||
class ListCategoryView(ListAPIView):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.urls import path
|
||||
|
||||
from django_otp.decorators import otp_required
|
||||
|
||||
from newsreader.news.core.endpoints import (
|
||||
CategoryReadView,
|
||||
DetailCategoryView,
|
||||
|
|
@ -14,20 +15,19 @@ from newsreader.news.core.views import (
|
|||
CategoryCreateView,
|
||||
CategoryListView,
|
||||
CategoryUpdateView,
|
||||
NewsView,
|
||||
)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("categories/", login_required(CategoryListView.as_view()), name="categories"),
|
||||
path("categories/", otp_required(CategoryListView.as_view()), name="categories"),
|
||||
path(
|
||||
"categories/<int:pk>/",
|
||||
login_required(CategoryUpdateView.as_view()),
|
||||
otp_required(CategoryUpdateView.as_view()),
|
||||
name="category-update",
|
||||
),
|
||||
path(
|
||||
"categories/create/",
|
||||
login_required(CategoryCreateView.as_view()),
|
||||
otp_required(CategoryCreateView.as_view()),
|
||||
name="category-create",
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.urls import include, path
|
||||
|
||||
from django_otp.decorators import otp_required
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.views import get_schema_view
|
||||
from two_factor.admin import AdminSiteOTPRequired
|
||||
|
|
@ -21,7 +21,7 @@ schema_view = get_schema_view(schema_info, patterns=api_patterns)
|
|||
admin.site.__class__ = AdminSiteOTPRequired
|
||||
|
||||
urlpatterns = [
|
||||
path("", login_required(NewsView.as_view()), name="index"),
|
||||
path("", otp_required(NewsView.as_view()), name="index"),
|
||||
path("", include((news_patterns, "news"))),
|
||||
path("", include((api_patterns, "api"))),
|
||||
path("accounts/", include((login_urls, "accounts")), name="accounts"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue