kopia lustrzana https://github.com/mediacms-io/mediacms
rodzic
86cc0442d8
commit
ba94989e6a
|
@ -29,7 +29,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Run Django Tests
|
- name: Run Django Tests
|
||||||
run: docker-compose -f docker-compose-dev.yaml exec -T web pytest
|
run: docker-compose -f docker-compose-dev.yaml exec --env TESTING=True -T web pytest
|
||||||
|
|
||||||
- name: Tear down the Stack
|
- name: Tear down the Stack
|
||||||
run: docker-compose -f docker-compose-dev.yaml down
|
run: docker-compose -f docker-compose-dev.yaml down
|
||||||
|
|
|
@ -441,6 +441,13 @@ LOCAL_INSTALL = False
|
||||||
# it is placed here so it can be overrided on local_settings.py
|
# it is placed here so it can be overrided on local_settings.py
|
||||||
GLOBAL_LOGIN_REQUIRED = False
|
GLOBAL_LOGIN_REQUIRED = False
|
||||||
|
|
||||||
|
# TODO: separate settings on production/development more properly, for now
|
||||||
|
# this should be ok
|
||||||
|
CELERY_TASK_ALWAYS_EAGER = False
|
||||||
|
if os.environ.get("TESTING"):
|
||||||
|
CELERY_TASK_ALWAYS_EAGER = True
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# keep a local_settings.py file for local overrides
|
# keep a local_settings.py file for local overrides
|
||||||
from .local_settings import * # noqa
|
from .local_settings import * # noqa
|
||||||
|
|
14
cms/urls.py
14
cms/urls.py
|
@ -1,7 +1,7 @@
|
||||||
import debug_toolbar
|
import debug_toolbar
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, re_path
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path, re_path
|
from django.urls import path
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from drf_yasg.views import get_schema_view
|
from drf_yasg.views import get_schema_view
|
||||||
|
@ -15,15 +15,15 @@ schema_view = get_schema_view(
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r"^__debug__/", include(debug_toolbar.urls)),
|
re_path(r"^__debug__/", include(debug_toolbar.urls)),
|
||||||
path(
|
path(
|
||||||
"robots.txt",
|
"robots.txt",
|
||||||
TemplateView.as_view(template_name="robots.txt", content_type="text/plain"),
|
TemplateView.as_view(template_name="robots.txt", content_type="text/plain"),
|
||||||
),
|
),
|
||||||
url(r"^", include("files.urls")),
|
re_path(r"^", include("files.urls")),
|
||||||
url(r"^", include("users.urls")),
|
re_path(r"^", include("users.urls")),
|
||||||
url(r"^accounts/", include("allauth.urls")),
|
re_path(r"^accounts/", include("allauth.urls")),
|
||||||
url(r"^api-auth/", include("rest_framework.urls")),
|
re_path(r"^api-auth/", include("rest_framework.urls")),
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
|
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
|
||||||
re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
from .user_utils import create_account # noqa
|
|
@ -0,0 +1,24 @@
|
||||||
|
from faker import Factory
|
||||||
|
|
||||||
|
from users.models import User
|
||||||
|
|
||||||
|
faker = Factory.create()
|
||||||
|
|
||||||
|
|
||||||
|
def create_account(username=None, email=None, password=None, name=None, **kwargs):
|
||||||
|
"Allow to create accounts by passing None or specific arguements"
|
||||||
|
email = email or faker.email()
|
||||||
|
username = username or email.split('a')[0]
|
||||||
|
password = password or faker.password()
|
||||||
|
name = name or faker.name()
|
||||||
|
|
||||||
|
description = kwargs.get('description') or faker.text()
|
||||||
|
is_superuser = kwargs.get('is_superuser') or False
|
||||||
|
is_manager = kwargs.get('is_manager') or False
|
||||||
|
is_editor = kwargs.get('is_editor') or False
|
||||||
|
|
||||||
|
user = User.objects.create(username=username, email=email, name=name, description=description, is_superuser=is_superuser, is_staff=is_superuser, is_editor=is_editor, is_manager=is_manager)
|
||||||
|
|
||||||
|
user.set_password(password)
|
||||||
|
user.save()
|
||||||
|
return user
|
100
files/urls.py
100
files/urls.py
|
@ -1,5 +1,5 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, re_path
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
|
@ -7,85 +7,85 @@ from . import management_views, views
|
||||||
from .feeds import IndexRSSFeed, SearchRSSFeed
|
from .feeds import IndexRSSFeed, SearchRSSFeed
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r"^$", views.index),
|
re_path(r"^$", views.index),
|
||||||
url(r"^about", views.about, name="about"),
|
re_path(r"^about", views.about, name="about"),
|
||||||
url(r"^add_subtitle", views.add_subtitle, name="add_subtitle"),
|
re_path(r"^add_subtitle", views.add_subtitle, name="add_subtitle"),
|
||||||
url(r"^categories$", views.categories, name="categories"),
|
re_path(r"^categories$", views.categories, name="categories"),
|
||||||
url(r"^contact$", views.contact, name="contact"),
|
re_path(r"^contact$", views.contact, name="contact"),
|
||||||
url(r"^edit", views.edit_media, name="edit_media"),
|
re_path(r"^edit", views.edit_media, name="edit_media"),
|
||||||
url(r"^embed", views.embed_media, name="get_embed"),
|
re_path(r"^embed", views.embed_media, name="get_embed"),
|
||||||
url(r"^featured$", views.featured_media),
|
re_path(r"^featured$", views.featured_media),
|
||||||
url(r"^fu/", include(("uploader.urls", "uploader"), namespace="uploader")),
|
re_path(r"^fu/", include(("uploader.urls", "uploader"), namespace="uploader")),
|
||||||
url(r"^history$", views.history, name="history"),
|
re_path(r"^history$", views.history, name="history"),
|
||||||
url(r"^liked$", views.liked_media, name="liked_media"),
|
re_path(r"^liked$", views.liked_media, name="liked_media"),
|
||||||
url(r"^latest$", views.latest_media),
|
re_path(r"^latest$", views.latest_media),
|
||||||
url(r"^members", views.members, name="members"),
|
re_path(r"^members", views.members, name="members"),
|
||||||
url(
|
re_path(
|
||||||
r"^playlist/(?P<friendly_token>[\w]*)$",
|
r"^playlist/(?P<friendly_token>[\w]*)$",
|
||||||
views.view_playlist,
|
views.view_playlist,
|
||||||
name="get_playlist",
|
name="get_playlist",
|
||||||
),
|
),
|
||||||
url(
|
re_path(
|
||||||
r"^playlists/(?P<friendly_token>[\w]*)$",
|
r"^playlists/(?P<friendly_token>[\w]*)$",
|
||||||
views.view_playlist,
|
views.view_playlist,
|
||||||
name="get_playlist",
|
name="get_playlist",
|
||||||
),
|
),
|
||||||
url(r"^popular$", views.recommended_media),
|
re_path(r"^popular$", views.recommended_media),
|
||||||
url(r"^recommended$", views.recommended_media),
|
re_path(r"^recommended$", views.recommended_media),
|
||||||
path("rss/", IndexRSSFeed()),
|
path("rss/", IndexRSSFeed()),
|
||||||
url("^rss/search", SearchRSSFeed()),
|
re_path("^rss/search", SearchRSSFeed()),
|
||||||
url(r"^search", views.search, name="search"),
|
re_path(r"^search", views.search, name="search"),
|
||||||
url(r"^scpublisher", views.upload_media, name="upload_media"),
|
re_path(r"^scpublisher", views.upload_media, name="upload_media"),
|
||||||
url(r"^tags", views.tags, name="tags"),
|
re_path(r"^tags", views.tags, name="tags"),
|
||||||
url(r"^tos$", views.tos, name="terms_of_service"),
|
re_path(r"^tos$", views.tos, name="terms_of_service"),
|
||||||
url(r"^view", views.view_media, name="get_media"),
|
re_path(r"^view", views.view_media, name="get_media"),
|
||||||
url(r"^upload", views.upload_media, name="upload_media"),
|
re_path(r"^upload", views.upload_media, name="upload_media"),
|
||||||
# API VIEWS
|
# API VIEWS
|
||||||
url(r"^api/v1/media$", views.MediaList.as_view()),
|
re_path(r"^api/v1/media$", views.MediaList.as_view()),
|
||||||
url(r"^api/v1/media/$", views.MediaList.as_view()),
|
re_path(r"^api/v1/media/$", views.MediaList.as_view()),
|
||||||
url(
|
re_path(
|
||||||
r"^api/v1/media/(?P<friendly_token>[\w]*)$",
|
r"^api/v1/media/(?P<friendly_token>[\w]*)$",
|
||||||
views.MediaDetail.as_view(),
|
views.MediaDetail.as_view(),
|
||||||
name="api_get_media",
|
name="api_get_media",
|
||||||
),
|
),
|
||||||
url(
|
re_path(
|
||||||
r"^api/v1/media/encoding/(?P<encoding_id>[\w]*)$",
|
r"^api/v1/media/encoding/(?P<encoding_id>[\w]*)$",
|
||||||
views.EncodingDetail.as_view(),
|
views.EncodingDetail.as_view(),
|
||||||
name="api_get_encoding",
|
name="api_get_encoding",
|
||||||
),
|
),
|
||||||
url(r"^api/v1/search$", views.MediaSearch.as_view()),
|
re_path(r"^api/v1/search$", views.MediaSearch.as_view()),
|
||||||
url(
|
re_path(
|
||||||
r"^api/v1/media/(?P<friendly_token>[\w]*)/actions$",
|
r"^api/v1/media/(?P<friendly_token>[\w]*)/actions$",
|
||||||
views.MediaActions.as_view(),
|
views.MediaActions.as_view(),
|
||||||
),
|
),
|
||||||
url(r"^api/v1/categories$", views.CategoryList.as_view()),
|
re_path(r"^api/v1/categories$", views.CategoryList.as_view()),
|
||||||
url(r"^api/v1/tags$", views.TagList.as_view()),
|
re_path(r"^api/v1/tags$", views.TagList.as_view()),
|
||||||
url(r"^api/v1/comments$", views.CommentList.as_view()),
|
re_path(r"^api/v1/comments$", views.CommentList.as_view()),
|
||||||
url(
|
re_path(
|
||||||
r"^api/v1/media/(?P<friendly_token>[\w]*)/comments$",
|
r"^api/v1/media/(?P<friendly_token>[\w]*)/comments$",
|
||||||
views.CommentDetail.as_view(),
|
views.CommentDetail.as_view(),
|
||||||
),
|
),
|
||||||
url(
|
re_path(
|
||||||
r"^api/v1/media/(?P<friendly_token>[\w]*)/comments/(?P<uid>[\w-]*)$",
|
r"^api/v1/media/(?P<friendly_token>[\w]*)/comments/(?P<uid>[\w-]*)$",
|
||||||
views.CommentDetail.as_view(),
|
views.CommentDetail.as_view(),
|
||||||
),
|
),
|
||||||
url(r"^api/v1/playlists$", views.PlaylistList.as_view()),
|
re_path(r"^api/v1/playlists$", views.PlaylistList.as_view()),
|
||||||
url(r"^api/v1/playlists/$", views.PlaylistList.as_view()),
|
re_path(r"^api/v1/playlists/$", views.PlaylistList.as_view()),
|
||||||
url(
|
re_path(
|
||||||
r"^api/v1/playlists/(?P<friendly_token>[\w]*)$",
|
r"^api/v1/playlists/(?P<friendly_token>[\w]*)$",
|
||||||
views.PlaylistDetail.as_view(),
|
views.PlaylistDetail.as_view(),
|
||||||
name="api_get_playlist",
|
name="api_get_playlist",
|
||||||
),
|
),
|
||||||
url(r"^api/v1/user/action/(?P<action>[\w]*)$", views.UserActions.as_view()),
|
re_path(r"^api/v1/user/action/(?P<action>[\w]*)$", views.UserActions.as_view()),
|
||||||
# ADMIN VIEWS
|
# ADMIN VIEWS
|
||||||
url(r"^api/v1/encode_profiles/$", views.EncodeProfileList.as_view()),
|
re_path(r"^api/v1/encode_profiles/$", views.EncodeProfileList.as_view()),
|
||||||
url(r"^api/v1/manage_media$", management_views.MediaList.as_view()),
|
re_path(r"^api/v1/manage_media$", management_views.MediaList.as_view()),
|
||||||
url(r"^api/v1/manage_comments$", management_views.CommentList.as_view()),
|
re_path(r"^api/v1/manage_comments$", management_views.CommentList.as_view()),
|
||||||
url(r"^api/v1/manage_users$", management_views.UserList.as_view()),
|
re_path(r"^api/v1/manage_users$", management_views.UserList.as_view()),
|
||||||
url(r"^api/v1/tasks$", views.TasksList.as_view()),
|
re_path(r"^api/v1/tasks$", views.TasksList.as_view()),
|
||||||
url(r"^api/v1/tasks/$", views.TasksList.as_view()),
|
re_path(r"^api/v1/tasks/$", views.TasksList.as_view()),
|
||||||
url(r"^api/v1/tasks/(?P<friendly_token>[\w|\W]*)$", views.TaskDetail.as_view()),
|
re_path(r"^api/v1/tasks/(?P<friendly_token>[\w|\W]*)$", views.TaskDetail.as_view()),
|
||||||
url(r"^manage/comments$", views.manage_comments, name="manage_comments"),
|
re_path(r"^manage/comments$", views.manage_comments, name="manage_comments"),
|
||||||
url(r"^manage/media$", views.manage_media, name="manage_media"),
|
re_path(r"^manage/media$", views.manage_media, name="manage_media"),
|
||||||
url(r"^manage/users$", views.manage_users, name="manage_users"),
|
re_path(r"^manage/users$", views.manage_users, name="manage_users"),
|
||||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
|
@ -4,6 +4,8 @@ import sys
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings")
|
||||||
|
os.environ.setdefault("TESTING", "True")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
from django.test import Client, TestCase
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
|
||||||
|
from files.tests import create_account
|
||||||
|
|
||||||
|
API_V1_LOGIN_URL = '/api/v1/login'
|
||||||
|
|
||||||
|
|
||||||
|
class TestUserLogin(TestCase):
|
||||||
|
fixtures = ["fixtures/categories.json", "fixtures/encoding_profiles.json"]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.password = 'this_is_a_fake_password'
|
||||||
|
self.user = create_account(password=self.password)
|
||||||
|
|
||||||
|
def test_login_endpoint(self):
|
||||||
|
client = Client()
|
||||||
|
response = client.get(API_V1_LOGIN_URL)
|
||||||
|
self.assertEqual(
|
||||||
|
response.status_code,
|
||||||
|
405,
|
||||||
|
"GET not allowed here",
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.post(API_V1_LOGIN_URL, {'username': 'fake', 'password': 'fake'})
|
||||||
|
self.assertTrue('User not found' in str(response.content), 'Expected user not to be there')
|
||||||
|
|
||||||
|
user = self.user
|
||||||
|
response = client.post(API_V1_LOGIN_URL, {'username': user.username, 'password': self.password})
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
response.status_code,
|
||||||
|
200,
|
||||||
|
"Expected 200",
|
||||||
|
)
|
||||||
|
data = response.data
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
data.get('email'),
|
||||||
|
user.email,
|
||||||
|
"Expected user email",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
data.get('username'),
|
||||||
|
user.username,
|
||||||
|
"Expected username",
|
||||||
|
)
|
||||||
|
|
||||||
|
token = Token.objects.filter(user=user).first()
|
||||||
|
self.assertEqual(
|
||||||
|
data.get('token'),
|
||||||
|
token.key,
|
||||||
|
"Expected valid token",
|
||||||
|
)
|
|
@ -0,0 +1,49 @@
|
||||||
|
from django.test import Client, TestCase
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
|
||||||
|
from files.tests import create_account
|
||||||
|
|
||||||
|
API_V1_USER_TOKEN_URL = '/api/v1/user/token'
|
||||||
|
|
||||||
|
|
||||||
|
class TestUserToken(TestCase):
|
||||||
|
fixtures = ["fixtures/categories.json", "fixtures/encoding_profiles.json"]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.password = 'this_is_a_fake_password'
|
||||||
|
self.user = create_account(password=self.password)
|
||||||
|
|
||||||
|
def test_user_token_endpoint(self):
|
||||||
|
client = Client()
|
||||||
|
response = client.get(API_V1_USER_TOKEN_URL)
|
||||||
|
self.assertEqual(
|
||||||
|
response.status_code,
|
||||||
|
403,
|
||||||
|
"FORBIDDEN",
|
||||||
|
)
|
||||||
|
|
||||||
|
user = self.user
|
||||||
|
client.force_login(user=user)
|
||||||
|
|
||||||
|
response = client.post(API_V1_USER_TOKEN_URL)
|
||||||
|
self.assertEqual(
|
||||||
|
response.status_code,
|
||||||
|
405,
|
||||||
|
"method not allowed here",
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(API_V1_USER_TOKEN_URL)
|
||||||
|
data = response.data
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
response.status_code,
|
||||||
|
200,
|
||||||
|
"expected 200",
|
||||||
|
)
|
||||||
|
|
||||||
|
token = Token.objects.filter(user=user).first()
|
||||||
|
self.assertEqual(
|
||||||
|
data.get('token'),
|
||||||
|
token.key,
|
||||||
|
"Expected valid token",
|
||||||
|
)
|
|
@ -0,0 +1,41 @@
|
||||||
|
from django.test import Client, TestCase
|
||||||
|
|
||||||
|
from files.tests import create_account
|
||||||
|
|
||||||
|
API_V1_LOGIN_URL = '/api/v1/whoami'
|
||||||
|
|
||||||
|
|
||||||
|
class TestUserWhoami(TestCase):
|
||||||
|
fixtures = ["fixtures/categories.json", "fixtures/encoding_profiles.json"]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.user = create_account()
|
||||||
|
|
||||||
|
def test_whoami_endpoint(self):
|
||||||
|
client = Client()
|
||||||
|
response = client.get(API_V1_LOGIN_URL)
|
||||||
|
self.assertEqual(
|
||||||
|
response.status_code,
|
||||||
|
403,
|
||||||
|
"Expected 403",
|
||||||
|
)
|
||||||
|
|
||||||
|
user = self.user
|
||||||
|
client.force_login(user=user)
|
||||||
|
response = client.get(API_V1_LOGIN_URL)
|
||||||
|
self.assertEqual(
|
||||||
|
response.status_code,
|
||||||
|
200,
|
||||||
|
"Expected 200",
|
||||||
|
)
|
||||||
|
data = response.data
|
||||||
|
self.assertEqual(
|
||||||
|
data.get('description'),
|
||||||
|
user.description,
|
||||||
|
"Expected user description",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
data.get('username'),
|
||||||
|
user.username,
|
||||||
|
"Expected username",
|
||||||
|
)
|
|
@ -1,4 +0,0 @@
|
||||||
def test_new_user(user_factory):
|
|
||||||
print(user_factory.name)
|
|
||||||
print(user_factory.description)
|
|
||||||
assert True
|
|
|
@ -1,10 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from django.conf.urls import url
|
from django.conf.urls import re_path
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
app_name = "uploader"
|
app_name = "uploader"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r"^upload/$", views.FineUploaderView.as_view(), name="upload"),
|
re_path(r"^upload/$", views.FineUploaderView.as_view(), name="upload"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
|
from files.methods import is_mediacms_manager
|
||||||
|
|
||||||
from .models import Channel, User
|
from .models import Channel, User
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +19,6 @@ class UserForm(forms.ModelForm):
|
||||||
fields = (
|
fields = (
|
||||||
"name",
|
"name",
|
||||||
"description",
|
"description",
|
||||||
"email",
|
|
||||||
"logo",
|
"logo",
|
||||||
"notification_on_comments",
|
"notification_on_comments",
|
||||||
"is_featured",
|
"is_featured",
|
||||||
|
@ -39,7 +40,7 @@ class UserForm(forms.ModelForm):
|
||||||
def __init__(self, user, *args, **kwargs):
|
def __init__(self, user, *args, **kwargs):
|
||||||
super(UserForm, self).__init__(*args, **kwargs)
|
super(UserForm, self).__init__(*args, **kwargs)
|
||||||
self.fields.pop("is_featured")
|
self.fields.pop("is_featured")
|
||||||
if not user.is_superuser:
|
if not is_mediacms_manager(user):
|
||||||
self.fields.pop("advancedUser")
|
self.fields.pop("advancedUser")
|
||||||
self.fields.pop("is_manager")
|
self.fields.pop("is_manager")
|
||||||
self.fields.pop("is_editor")
|
self.fields.pop("is_editor")
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth import authenticate
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
|
||||||
from .models import User
|
from .models import User
|
||||||
|
|
||||||
|
@ -77,3 +80,46 @@ class UserDetailSerializer(serializers.ModelSerializer):
|
||||||
"default_channel_edit_url",
|
"default_channel_edit_url",
|
||||||
)
|
)
|
||||||
extra_kwargs = {"name": {"required": False}}
|
extra_kwargs = {"name": {"required": False}}
|
||||||
|
|
||||||
|
|
||||||
|
class LoginSerializer(serializers.Serializer):
|
||||||
|
email = serializers.CharField(max_length=255, required=False)
|
||||||
|
username = serializers.CharField(max_length=255, required=False)
|
||||||
|
password = serializers.CharField(max_length=128, write_only=True)
|
||||||
|
token = serializers.CharField(max_length=255, required=False)
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
email = data.get('email', None)
|
||||||
|
username = data.get('username', None)
|
||||||
|
password = data.get('password', None)
|
||||||
|
|
||||||
|
if settings.ACCOUNT_AUTHENTICATION_METHOD == 'username' and not username:
|
||||||
|
raise serializers.ValidationError('username is required to log in.')
|
||||||
|
else:
|
||||||
|
username_or_email = username
|
||||||
|
if settings.ACCOUNT_AUTHENTICATION_METHOD == 'email' and not email:
|
||||||
|
raise serializers.ValidationError('email is required to log in.')
|
||||||
|
else:
|
||||||
|
username_or_email = email
|
||||||
|
|
||||||
|
if settings.ACCOUNT_AUTHENTICATION_METHOD == 'username_email' and not (username or email):
|
||||||
|
raise serializers.ValidationError('username or email is required to log in.')
|
||||||
|
else:
|
||||||
|
username_or_email = username or email
|
||||||
|
|
||||||
|
if password is None:
|
||||||
|
raise serializers.ValidationError('password is required to log in.')
|
||||||
|
|
||||||
|
user = authenticate(username=username_or_email, password=password)
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
raise serializers.ValidationError('User not found.')
|
||||||
|
|
||||||
|
if not user.is_active:
|
||||||
|
raise serializers.ValidationError('User has been deactivated.')
|
||||||
|
|
||||||
|
token = Token.objects.filter(user=user).first()
|
||||||
|
if not token:
|
||||||
|
token = Token.objects.create(user=user)
|
||||||
|
|
||||||
|
return {'email': user.email, 'username': user.username, 'token': token.key}
|
||||||
|
|
|
@ -1,41 +1,45 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import re_path
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r"^user/(?P<username>[\w@._-]*)$", views.view_user, name="get_user"),
|
re_path(r"^user/(?P<username>[\w@._-]*)$", views.view_user, name="get_user"),
|
||||||
url(r"^user/(?P<username>[\w@._-]*)/$", views.view_user, name="get_user"),
|
re_path(r"^user/(?P<username>[\w@._-]*)/$", views.view_user, name="get_user"),
|
||||||
url(
|
re_path(
|
||||||
r"^user/(?P<username>[\w@.]*)/media$",
|
r"^user/(?P<username>[\w@.]*)/media$",
|
||||||
views.view_user_media,
|
views.view_user_media,
|
||||||
name="get_user_media",
|
name="get_user_media",
|
||||||
),
|
),
|
||||||
url(
|
re_path(
|
||||||
r"^user/(?P<username>[\w@.]*)/playlists$",
|
r"^user/(?P<username>[\w@.]*)/playlists$",
|
||||||
views.view_user_playlists,
|
views.view_user_playlists,
|
||||||
name="get_user_playlists",
|
name="get_user_playlists",
|
||||||
),
|
),
|
||||||
url(
|
re_path(
|
||||||
r"^user/(?P<username>[\w@.]*)/about$",
|
r"^user/(?P<username>[\w@.]*)/about$",
|
||||||
views.view_user_about,
|
views.view_user_about,
|
||||||
name="get_user_about",
|
name="get_user_about",
|
||||||
),
|
),
|
||||||
url(r"^user/(?P<username>[\w@.]*)/edit$", views.edit_user, name="edit_user"),
|
re_path(r"^user/(?P<username>[\w@.]*)/edit$", views.edit_user, name="edit_user"),
|
||||||
url(r"^channel/(?P<friendly_token>[\w]*)$", views.view_channel, name="view_channel"),
|
re_path(r"^channel/(?P<friendly_token>[\w]*)$", views.view_channel, name="view_channel"),
|
||||||
url(
|
re_path(
|
||||||
r"^channel/(?P<friendly_token>[\w]*)/edit$",
|
r"^channel/(?P<friendly_token>[\w]*)/edit$",
|
||||||
views.edit_channel,
|
views.edit_channel,
|
||||||
name="edit_channel",
|
name="edit_channel",
|
||||||
),
|
),
|
||||||
# API VIEWS
|
# API VIEWS
|
||||||
url(r"^api/v1/users$", views.UserList.as_view(), name="api_users"),
|
path('api/v1/whoami', views.UserWhoami.as_view(), name='user-whoami'),
|
||||||
url(r"^api/v1/users/$", views.UserList.as_view()),
|
path('api/v1/user/token', views.UserToken.as_view(), name='user-token'),
|
||||||
url(
|
path('api/v1/login', views.LoginView.as_view(), name='user-login'),
|
||||||
|
re_path(r"^api/v1/users$", views.UserList.as_view(), name="api_users"),
|
||||||
|
re_path(r"^api/v1/users/$", views.UserList.as_view()),
|
||||||
|
re_path(
|
||||||
r"^api/v1/users/(?P<username>[\w@._-]*)$",
|
r"^api/v1/users/(?P<username>[\w@._-]*)$",
|
||||||
views.UserDetail.as_view(),
|
views.UserDetail.as_view(),
|
||||||
name="api_get_user",
|
name="api_get_user",
|
||||||
),
|
),
|
||||||
url(
|
re_path(
|
||||||
r"^api/v1/users/(?P<username>[\w@._-]*)/contact",
|
r"^api/v1/users/(?P<username>[\w@._-]*)/contact",
|
||||||
views.contact_user,
|
views.contact_user,
|
||||||
name="api_contact_user",
|
name="api_contact_user",
|
||||||
|
|
|
@ -5,7 +5,8 @@ from django.http import HttpResponseRedirect
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from drf_yasg import openapi as openapi
|
from drf_yasg import openapi as openapi
|
||||||
from drf_yasg.utils import swagger_auto_schema
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
from rest_framework import permissions, status
|
from rest_framework import generics, permissions, status
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
from rest_framework.decorators import api_view
|
from rest_framework.decorators import api_view
|
||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.parsers import (
|
from rest_framework.parsers import (
|
||||||
|
@ -23,7 +24,7 @@ from files.methods import is_mediacms_editor, is_mediacms_manager
|
||||||
|
|
||||||
from .forms import ChannelForm, UserForm
|
from .forms import ChannelForm, UserForm
|
||||||
from .models import Channel, User
|
from .models import Channel, User
|
||||||
from .serializers import UserDetailSerializer, UserSerializer
|
from .serializers import LoginSerializer, UserDetailSerializer, UserSerializer
|
||||||
|
|
||||||
|
|
||||||
def get_user(username):
|
def get_user(username):
|
||||||
|
@ -305,3 +306,65 @@ class UserDetail(APIView):
|
||||||
|
|
||||||
user.delete()
|
user.delete()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class UserWhoami(generics.RetrieveAPIView):
|
||||||
|
parser_classes = (JSONParser, FormParser, MultiPartParser)
|
||||||
|
queryset = User.objects.all()
|
||||||
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
|
serializer_class = UserDetailSerializer
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return User.objects.get(id=self.request.user.id)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
tags=['Users'],
|
||||||
|
operation_summary='Whoami user information',
|
||||||
|
operation_description='Whoami user information',
|
||||||
|
responses={200: openapi.Response('response description', UserDetailSerializer), 403: 'Forbidden'},
|
||||||
|
)
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
return super(UserWhoami, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class UserToken(APIView):
|
||||||
|
parser_classes = (JSONParser,)
|
||||||
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
tags=['Users'],
|
||||||
|
operation_summary='Get a user token',
|
||||||
|
operation_description="Returns an authenticated user's token",
|
||||||
|
responses={200: 'token', 403: 'Forbidden'},
|
||||||
|
)
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
token = Token.objects.filter(user=request.user).first()
|
||||||
|
if not token:
|
||||||
|
token = Token.objects.create(user=request.user)
|
||||||
|
|
||||||
|
return Response({'token': str(token)}, status=200)
|
||||||
|
|
||||||
|
|
||||||
|
class LoginView(APIView):
|
||||||
|
permission_classes = (permissions.AllowAny,)
|
||||||
|
serializer_class = LoginSerializer
|
||||||
|
parser_classes = (MultiPartParser, FormParser, FileUploadParser)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
tags=['Users'],
|
||||||
|
operation_summary='Login url',
|
||||||
|
operation_description="Login url endpoint. According to what the portal provides, you may provide username and/or email, plus the password",
|
||||||
|
manual_parameters=[
|
||||||
|
openapi.Parameter(name="username", in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=False, description="username"),
|
||||||
|
openapi.Parameter(name="email", in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=False, description="email"),
|
||||||
|
openapi.Parameter(name="password", in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=True, description="password"),
|
||||||
|
],
|
||||||
|
responses={200: openapi.Response('user details', LoginSerializer), 404: 'Bad request'},
|
||||||
|
)
|
||||||
|
def post(self, request):
|
||||||
|
data = request.data
|
||||||
|
|
||||||
|
serializer = self.serializer_class(data=data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
|
Ładowanie…
Reference in New Issue