Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
L
library-app-django
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Ilham Maulana
library-app-django
Commits
dbee2adc
Commit
dbee2adc
authored
Jul 20, 2024
by
Ilham Maulana
💻
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: shifting auth api to simple jwt
parent
91432e03
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
103 additions
and
118 deletions
+103
-118
permissions.py
api/auth/permissions.py
+18
-14
serializers.py
api/auth/serializers.py
+14
-0
views.py
api/auth/views.py
+48
-98
urls.py
api/urls.py
+6
-4
settings.py
config/settings.py
+11
-2
urls.py
config/urls.py
+6
-0
No files found.
api/auth/permissions.py
View file @
dbee2adc
from
rest_framework
import
permissions
from
rest_framework.permissions
import
IsAuthenticated
from
rest_framework_simplejwt.tokens
import
RefreshToken
class
IsStaffUser
(
permissions
.
BasePermission
):
class
IsStaffUser
(
IsAuthenticated
):
def
has_permission
(
self
,
request
,
view
):
if
request
.
method
!=
"POST"
and
not
request
.
user
.
is_staff
:
return
False
elif
request
.
method
!=
"POST"
and
not
request
.
user
.
is_authenticated
:
return
False
refresh_token
=
request
.
session
.
get
(
"refresh_token"
)
return
True
return
bool
(
refresh_token
is
not
None
and
request
.
user
and
request
.
user
.
is_authenticated
and
request
.
user
.
is_staff
)
class
IsNotStaffUser
(
permissions
.
BasePermission
):
class
IsNotStaffUser
(
IsAuthenticated
):
def
has_permission
(
self
,
request
,
view
):
if
request
.
method
!=
"POST"
and
request
.
user
.
is_staff
:
return
False
elif
request
.
method
!=
"POST"
and
not
request
.
user
.
is_authenticated
:
return
False
return
True
refresh_token
=
request
.
session
.
get
(
"refresh_token"
)
return
bool
(
refresh_token
is
not
None
and
request
.
user
and
request
.
user
.
is_authenticated
and
not
request
.
user
.
is_staff
)
api/auth/serializers.py
View file @
dbee2adc
from
django.contrib.auth
import
authenticate
from
rest_framework
import
serializers
from
users.models
import
User
,
Librarian
,
Member
,
LibrarianLoginHistory
...
...
@@ -40,7 +41,18 @@ class LibrarianSerializer(serializers.ModelSerializer):
def
create
(
self
,
validated_data
):
user_data
=
validated_data
.
pop
(
"user"
)
user_data
[
"is_staff"
]
=
True
username
=
user_data
.
get
(
"username"
)
email
=
user_data
.
get
(
"email"
)
is_username
=
User
.
objects
.
filter
(
username
=
username
)
is_email
=
User
.
objects
.
filter
(
email
=
email
)
if
is_username
.
exists
()
and
is_email
.
exists
():
raise
serializers
.
ValidationError
(
"Username or Email is already exists"
)
user
=
User
.
objects
.
create_user
(
**
user_data
)
user
.
set_password
(
user_data
.
get
(
"password"
))
user
.
save
()
librarian
=
Librarian
.
objects
.
create
(
user
=
user
,
**
validated_data
)
return
librarian
...
...
@@ -90,6 +102,8 @@ class MemberSerializer(serializers.ModelSerializer):
user_data
=
validated_data
.
pop
(
"user"
)
user_data
[
"is_staff"
]
=
False
user
=
User
.
objects
.
create_user
(
**
user_data
)
user
.
set_password
(
user_data
.
get
(
"password"
))
user
.
save
()
member
=
Member
.
objects
.
create
(
user
=
user
,
**
validated_data
)
return
member
...
...
api/auth/views.py
View file @
dbee2adc
from
django.contrib.auth
import
authenticate
,
login
,
logout
from
django.contrib.auth
import
authenticate
from
django.contrib.auth.tokens
import
default_token_generator
from
django.core.mail
import
send_mail
from
rest_framework
import
views
,
viewsets
,
status
from
rest_framework.response
import
Response
from
rest_framework.filters
import
SearchFilter
from
rest_framework_simplejwt.views
import
TokenObtainPairView
from
.serializers
import
(
User
,
...
...
@@ -39,8 +41,17 @@ class LibrarianViewSet(viewsets.ModelViewSet):
return
Response
(
serializer
.
data
,
status
=
status
.
HTTP_200_OK
)
class
LibrarianLoginHistoryViewSet
(
viewsets
.
ModelViewSet
):
permission_classes
=
[
IsStaffUser
]
queryset
=
LibrarianLoginHistory
.
objects
.
all
()
.
order_by
(
"date"
)
serializer_class
=
LoginHistorySerializer
filter_backends
=
[
SearchFilter
]
search_fields
=
[
"librarian__name"
]
class
MemberViewSet
(
viewsets
.
ModelViewSet
):
permission_classes
=
[
Is
Not
StaffUser
]
permission_classes
=
[
IsStaffUser
]
queryset
=
Member
.
objects
.
all
()
.
order_by
(
"created_at"
)
serializer_class
=
MemberSerializer
...
...
@@ -70,146 +81,85 @@ class MemberViewSet(viewsets.ModelViewSet):
return
Response
(
serializer
.
data
,
status
=
status
.
HTTP_200_OK
)
class
LoginBaseView
(
views
.
API
View
):
class
LoginBaseView
(
TokenObtainPair
View
):
user
=
None
def
post
(
self
,
request
):
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
response
=
super
()
.
post
(
request
,
*
args
,
**
kwargs
)
username
=
request
.
data
.
get
(
"username"
)
password
=
request
.
data
.
get
(
"password"
)
user
=
authenticate
(
username
=
username
,
password
=
password
)
if
request
.
user
.
is_authenticated
:
if
user
is
None
:
return
Response
(
{
"message"
:
"
Login failed, user is already authenticate
d"
},
{
"message"
:
"
Invalid username or passwor
d"
},
status
=
status
.
HTTP_403_FORBIDDEN
,
)
if
username
is
None
or
password
is
None
:
return
Response
(
{
"message"
:
"Login failed, username or password cannot be empty"
},
status
=
status
.
HTTP_400_BAD_REQUEST
,
)
user
=
authenticate
(
request
,
username
=
username
,
password
=
password
)
if
user
is
not
None
:
self
.
user
=
user
request
.
data
[
"token"
]
=
user
.
get_session_auth_hash
()
request
.
data
[
"message"
]
=
"Login successful"
return
Response
(
request
.
data
,
status
=
status
.
HTTP_200_OK
)
else
:
return
Response
(
{
"message"
:
"Login failed, invalid username or password"
},
status
=
status
.
HTTP_401_UNAUTHORIZED
,
)
request
.
session
[
"refresh_token"
]
=
response
.
data
.
get
(
"refresh"
)
return
response
class
LibrarianLoginView
(
LoginBaseView
):
def
post
(
self
,
request
):
response
=
super
()
.
post
(
request
)
if
response
.
status_code
==
status
.
HTTP_200_OK
:
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
response
=
super
()
.
post
(
request
,
*
args
,
**
kwargs
)
if
response
.
status_code
==
200
:
if
not
self
.
user
.
is_staff
:
return
Response
(
{
"message"
:
"
Login as librarian failed, account is not staff
"
},
{
"message"
:
"
Account does not have access
"
},
status
=
status
.
HTTP_403_FORBIDDEN
,
)
else
:
login
(
request
,
self
.
user
)
librarian
=
Librarian
.
objects
.
get
(
user
=
self
.
user
)
LibrarianLoginHistory
.
objects
.
create
(
librarian
=
librarian
)
pass
return
response
class
LibrarianLoginHistoryViewSet
(
viewsets
.
ModelViewSet
):
permission_classes
=
[
IsStaffUser
]
queryset
=
LibrarianLoginHistory
.
objects
.
all
()
.
order_by
(
"date"
)
serializer_class
=
LoginHistorySerializer
filter_backends
=
[
SearchFilter
]
search_fields
=
[
"librarian__name"
]
class
LibrarianViewSet
(
viewsets
.
ModelViewSet
):
permission_classes
=
[
IsStaffUser
]
queryset
=
Librarian
.
objects
.
all
()
.
order_by
(
"created_at"
)
serializer_class
=
LibrarianSerializer
class
LibrarianRegisterView
(
views
.
APIView
):
filter_backends
=
[
SearchFilter
]
search_fields
=
[
"user__username"
,
"user__email"
,
"user__first_name"
,
"user__last_name"
,
]
def
update
(
self
,
request
,
pk
):
instance
=
self
.
get_object
()
serializer
=
self
.
get_serializer
(
instance
,
data
=
request
.
data
,
partial
=
True
)
def
post
(
self
,
request
):
data
=
request
.
data
data
[
"message"
]
=
"Register as librarian success"
serializer
=
LibrarianSerializer
(
data
=
data
)
serializer
.
is_valid
(
raise_exception
=
True
)
serializer
.
save
()
return
Response
(
serializer
.
data
,
status
=
status
.
HTTP_200_OK
)
class
MemberLoginView
(
LoginBaseView
):
def
post
(
self
,
request
):
response
=
super
()
.
post
(
request
)
if
response
.
status_code
==
status
.
HTTP_200_OK
:
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
response
=
super
()
.
post
(
request
,
*
args
,
**
kwargs
)
if
response
.
status_code
==
200
:
if
self
.
user
.
is_staff
:
return
Response
(
{
"message"
:
"
Login failed, invalid username or password
"
},
status
=
status
.
HTTP_40
1_UNAUTHORIZED
,
{
"message"
:
"
Account does not have access
"
},
status
=
status
.
HTTP_40
3_FORBIDDEN
,
)
else
:
login
(
request
,
self
.
user
)
pass
return
response
class
LogoutBasedView
(
views
.
APIView
):
def
get
(
self
,
request
):
if
not
request
.
user
.
is_authenticated
:
return
Response
(
{
"message"
:
"Logout failed, user is unauthorized"
},
status
=
status
.
HTTP_401_UNAUTHORIZED
,
)
return
Response
({
"message"
:
"Logout success"
},
status
=
status
.
HTTP_200_OK
)
class
LibrarianLogoutView
(
LogoutBasedView
):
class
LogoutView
(
views
.
APIView
):
def
get
(
self
,
request
):
response
=
super
()
.
get
(
request
)
if
response
.
status_code
==
status
.
HTTP_200_OK
:
if
request
.
user
.
is_staff
:
logout
(
request
)
else
:
refresh
=
request
.
session
.
get
(
"refresh_token"
)
if
refresh
is
None
:
return
Response
(
{
"message"
:
"Logout failed, user is unauthorized
"
},
status
=
status
.
HTTP_401_UNAUTHORIZED
,
{
"detail"
:
"You do not have permission to perform this action.
"
},
status
=
status
.
HTTP_403_FORBIDDEN
,
)
return
response
class
MemberLogoutView
(
LogoutBasedView
):
def
get
(
self
,
request
):
response
=
super
()
.
get
(
request
)
if
response
.
status_code
==
status
.
HTTP_200_OK
:
if
not
request
.
user
.
is_staff
:
logout
(
request
)
else
:
return
Response
(
{
"message"
:
"Logout failed, user is unauthorized"
},
status
=
status
.
HTTP_401_UNAUTHORIZED
,
)
del
request
.
session
[
"refresh_token"
]
return
response
return
Response
({
"message"
:
"Logout success"
},
status
=
status
.
HTTP_200_OK
)
class
MemberChangePasswordView
(
views
.
APIView
):
...
...
api/urls.py
View file @
dbee2adc
...
...
@@ -4,12 +4,12 @@ from rest_framework import routers
from
.auth.views
import
(
LibrarianViewSet
,
LibrarianLoginView
,
Librarian
Logout
View
,
Librarian
Register
View
,
LibrarianLoginHistoryViewSet
,
MemberViewSet
,
MemberLoginView
,
MemberLogoutView
,
MemberChangePasswordView
,
LogoutView
,
TokenResetPasswordView
,
ResetPasswordConfirmView
,
)
...
...
@@ -56,10 +56,12 @@ urlpatterns = [
),
path
(
"librarians/auth/login"
,
LibrarianLoginView
.
as_view
(),
name
=
"librarian_login"
),
path
(
"librarians/auth/logout"
,
LibrarianLogoutView
.
as_view
(),
name
=
"librarian_logout"
"librarians/auth/register"
,
LibrarianRegisterView
.
as_view
(),
name
=
"librarian_register"
,
),
path
(
"auth/logout"
,
LogoutView
.
as_view
(),
name
=
"librarian_logout"
),
path
(
"members/auth/login"
,
MemberLoginView
.
as_view
(),
name
=
"member_login"
),
path
(
"members/auth/logout/"
,
MemberLogoutView
.
as_view
(),
name
=
"member_logout"
),
path
(
"members/<int:member_id>/change-password"
,
MemberChangePasswordView
.
as_view
(),
...
...
config/settings.py
View file @
dbee2adc
...
...
@@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/5.0/ref/settings/
"""
from
pathlib
import
Path
from
django.utils.timezone
import
timedelta
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR
=
Path
(
__file__
)
.
resolve
()
.
parent
.
parent
...
...
@@ -44,6 +45,7 @@ INSTALLED_APPS = [
"dashboard.apps.DashboardConfig"
,
# 3rd party
"rest_framework"
,
"rest_framework_simplejwt"
,
"django_filters"
,
]
...
...
@@ -53,14 +55,21 @@ INSTALLED_APPS = [
REST_FRAMEWORK
=
{
"DEFAULT_AUTHENTICATION_CLASSES"
:
[
"rest_framework.authentication.SessionAuthentication"
,
"rest_framework.authentication.TokenAuthentication"
,
"rest_framework_simplejwt.authentication.JWTAuthentication"
,
],
"DEFAULT_FILTER_BACKENDS"
:
[
"django_filters.rest_framework.DjangoFilterBackend"
],
"DEFAULT_PAGINATION_CLASS"
:
"rest_framework.pagination.PageNumberPagination"
,
"PAGE_SIZE"
:
10
,
}
SIMPLE_JWT
=
{
"ACCESS_TOKEN_LIFETIME"
:
timedelta
(
minutes
=
60
),
"SLIDING_TOKEN_REFRESH_LIFETIME"
:
timedelta
(
days
=
1
),
"SLIDING_TOKEN_LIFETIME"
:
timedelta
(
days
=
30
),
"SLIDING_TOKEN_REFRESH_LIFETIME_LATE_USER"
:
timedelta
(
days
=
1
),
"SLIDING_TOKEN_LIFETIME_LATE_USER"
:
timedelta
(
days
=
30
),
}
# end 3rd party config
MIDDLEWARE
=
[
...
...
config/urls.py
View file @
dbee2adc
...
...
@@ -23,6 +23,10 @@ from django.contrib.auth.views import (
PasswordResetConfirmView
,
PasswordResetCompleteView
,
)
from
rest_framework_simplejwt.views
import
(
TokenObtainPairView
,
TokenRefreshView
,
)
from
dashboard.views
import
UpcomingLoanView
,
OverduedLoanView
from
users.views
import
(
...
...
@@ -42,6 +46,8 @@ urlpatterns = [
path
(
"upcoming-loans/"
,
UpcomingLoanView
.
as_view
(),
name
=
"upcoming_loans"
),
path
(
"overdued-loans/"
,
OverduedLoanView
.
as_view
(),
name
=
"overdued_loans"
),
# auth
path
(
"api/token/"
,
TokenObtainPairView
.
as_view
(),
name
=
"token_obtain_pair"
),
path
(
"api/token/refresh/"
,
TokenRefreshView
.
as_view
(),
name
=
"token_refresh"
),
path
(
"auth/login/"
,
LibrarianLoginView
.
as_view
(),
name
=
"librarian_login"
),
path
(
"auth/logout/"
,
LibrarianLogoutView
.
as_view
(),
name
=
"librarian_logout"
),
path
(
"auth/sign-up/"
,
LibrarianSignUpView
.
as_view
(),
name
=
"librarian_logout"
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment