From 5ab8aaecf3af7ab009d52fd1572baff7b11ff85d Mon Sep 17 00:00:00 2001 From: Choi Yeongjun Date: Thu, 30 May 2019 18:21:59 +0900 Subject: [PATCH 1/4] Append a drf authentication example --- sample_drf_middleware/README.md | 65 +++++++++++++++++++++++++ sample_drf_middleware/authentication.py | 41 ++++++++++++++++ sample_drf_middleware/cms.py | 27 ++++++++++ sample_drf_middleware/permissions.py | 40 +++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 sample_drf_middleware/README.md create mode 100644 sample_drf_middleware/authentication.py create mode 100644 sample_drf_middleware/cms.py create mode 100644 sample_drf_middleware/permissions.py diff --git a/sample_drf_middleware/README.md b/sample_drf_middleware/README.md new file mode 100644 index 0000000..e0ab26e --- /dev/null +++ b/sample_drf_middleware/README.md @@ -0,0 +1,65 @@ +# CMS Bootstrap for DRF(django rest framework) Middleware + +This template is designed for more ease of use when using django rest framework authentication with CMS-RPC + +You will see three files in the current directory. +- cms.py +- permissions.py +- authentication.py + +**What is cms.py** +The cms.py file preconfigures cms-sdk with RPC url. + +**What is permissions.py and authentication.py?** +These files are responsible for authentication and permissions based on the cms-sdk by preconfigured cms.py. + +If you want to understand this more, check out the [basic drf authentication and permissions](https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/). + + +# How to use +1. Save the files to the appropriate path in the project. +- cms.py +- permissions.py +- authentication.py + +2. Override the import path in the files. +```python +# permissions.py +from ${PROJECT_PATH}.cms import admin_auth + +# authentication.py +from ${PROJECT_PATH}.cms import admin_user, admin_auth +``` + +3. Defines the variables to use for authentication +```python +# settings.py + +... + +REST_FRAMEWORK = { + ... + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'PROJECT_PATH.authentication.CmsOpenAuthentication', + ), + ... +} + +... + +CMS_TEST_ID = os.getenv('TEST_ID', 'admin') +CMS_RPC_URL = os.getenv('CMS_RPC_URL', 'http://localhost') +CMS_AUTH_DISABLE = os.environ.get('CMS_AUTH_DISABLE', False) +... + +``` + +4. Enjoy it! example +``` +... +class UserListCreateAPIView(generics.ListCreateAPIView): + required_tags = ["example",] # Minimum permissions to access this API View + queryset = User.objects.all() + serializer_class = UserSerializer + permission_classes=(IsAuthenticated,) +``` \ No newline at end of file diff --git a/sample_drf_middleware/authentication.py b/sample_drf_middleware/authentication.py new file mode 100644 index 0000000..454278b --- /dev/null +++ b/sample_drf_middleware/authentication.py @@ -0,0 +1,41 @@ +import json +import logging + +from django.conf import settings +from django.core.exceptions import PermissionDenied +from django.contrib.auth.hashers import check_password +from django.contrib.auth import authenticate, login, get_user_model +from django.contrib.auth.backends import ModelBackend +from django.shortcuts import redirect + +from rest_framework import authentication +from rest_framework import exceptions + +from ridi.cms.login_session import COOKIE_CMS_TOKEN, COOKIE_ADMIN_ID +from ridi.cms.thrift.Errors.ttypes import * + +from ${PROJECT_PATH}.cms import admin_user, admin_auth + +User = get_user_model() +logger = logging.getLogger(__name__) + +class CmsOpenAuthentication(authentication.BaseAuthentication): + def authenticate(self, request): + username = self.__get_username(request) + if not username: + return None + + try: + user = User.objects.get(id=username) + except User.DoesNotExist: + raise exceptions.AuthenticationFailed('No such user') + + return (user, None) + + def __get_username(self, request): + if settings.DEBUG and settings.CMS_TEST_ID: + username = settings.CMS_TEST_ID + logger.debug('you have been permitted as ' + username) + return username + + return request.COOKIES.get(COOKIE_ADMIN_ID, None) \ No newline at end of file diff --git a/sample_drf_middleware/cms.py b/sample_drf_middleware/cms.py new file mode 100644 index 0000000..172260d --- /dev/null +++ b/sample_drf_middleware/cms.py @@ -0,0 +1,27 @@ +import os +from ridi.cms.cms_client import AdminUser, AdminAuth, AdminMenu +from ridi.cms.config import Config as CmsConfig + +from django.conf import settings + +config = CmsConfig() + +if settings.DEBUG: + config.RPC_URL = settings.CMS_RPC_URL +else: + import ssl + + try: + _create_unverified_https_context = ssl._create_unverified_context + except AttributeError: + # Legacy Python that doesn't verify HTTPS certificates by default + pass + else: + # Handle target environment that doesn't support HTTPS verification + ssl._create_default_https_context = _create_unverified_https_context + + config.RPC_URL = 'https://YOUR_CMS_RPC_HTTPS_DOMAIN' + +admin_user = AdminUser(config) +admin_auth = AdminAuth(config) +admin_menu = AdminMenu(config) diff --git a/sample_drf_middleware/permissions.py b/sample_drf_middleware/permissions.py new file mode 100644 index 0000000..73c1b06 --- /dev/null +++ b/sample_drf_middleware/permissions.py @@ -0,0 +1,40 @@ + +from django.conf import settings +from django.shortcuts import redirect + +from rest_framework import permissions +from rest_framework.exceptions import PermissionDenied +from rest_framework.permissions import BasePermission, IsAuthenticated, SAFE_METHODS + +from ridi.cms.login_session import COOKIE_CMS_TOKEN, COOKIE_ADMIN_ID +from ridi.cms.thrift.Errors.ttypes import NoTokenException, MalformedTokenException, ExpiredTokenException, UnauthorizedException, TException + +from ${PROJECT_PATH}.cms import admin_auth + +class IsOwnerOrReadOnly(permissions.BasePermission): + """ + Read only method, or owners with write permission. + """ + def has_permission(self, request, view): + if settings.DEBUG and settings.CMS_AUTH_DISABLE: + return True # Bypass at Debugging + + if request.method in permissions.SAFE_METHODS: + return True # Allow readonly access + + token = request.COOKIES.get(COOKIE_CMS_TOKEN, None) + try: + required_tags = self.__get_required_tags(view) + token = request.COOKIES.get(COOKIE_CMS_TOKEN, None) + admin_auth.authorizeByTag(token, required_tags) + except (NoTokenException, MalformedTokenException, ExpiredTokenException, UnauthorizedException) as e: + raise e + except (TException) as e: + return False + + return True + + def __get_required_tags(self, view, tags=[]): + if hasattr(view, 'required_tags'): + tags = views.required_tags + return tags \ No newline at end of file From 45455c28cb2c51da19f5a973eaf27c5594e8c5ea Mon Sep 17 00:00:00 2001 From: Choi Yeongjun Date: Thu, 30 May 2019 18:22:14 +0900 Subject: [PATCH 2/4] Add a description to introduce drf example --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 16c044d..1e47d3c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ A Python example project for CMS-SDK. This example uses docker images provided by [cms-docker-compose](https://github.com/ridi/cms-docker-compose). Learn more details about the docker images in the link. +> If you need Django rest framework exmaple; [click here](sample_drf_middleware/README.md) + ## Requirements - composer From 53d1bd967c1b46f7d926083143324255d809b6ea Mon Sep 17 00:00:00 2001 From: Choi Yeongjun Date: Thu, 30 May 2019 18:26:43 +0900 Subject: [PATCH 3/4] Update README.md --- sample_drf_middleware/README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sample_drf_middleware/README.md b/sample_drf_middleware/README.md index e0ab26e..c50cbbf 100644 --- a/sample_drf_middleware/README.md +++ b/sample_drf_middleware/README.md @@ -23,15 +23,20 @@ If you want to understand this more, check out the [basic drf authentication and - authentication.py 2. Override the import path in the files. + ```python # permissions.py from ${PROJECT_PATH}.cms import admin_auth # authentication.py from ${PROJECT_PATH}.cms import admin_user, admin_auth + +# cms.py +config.RPC_URL = 'https://YOUR_CMS_RPC_HTTPS_DOMAIN' ``` 3. Defines the variables to use for authentication + ```python # settings.py @@ -55,8 +60,8 @@ CMS_AUTH_DISABLE = os.environ.get('CMS_AUTH_DISABLE', False) ``` 4. Enjoy it! example -``` -... + +```python class UserListCreateAPIView(generics.ListCreateAPIView): required_tags = ["example",] # Minimum permissions to access this API View queryset = User.objects.all() From 933bae03ddc8724e3e32a373e75573c5222dc2a5 Mon Sep 17 00:00:00 2001 From: Choi Yeongjun Date: Thu, 30 May 2019 18:27:52 +0900 Subject: [PATCH 4/4] Update README.md --- sample_drf_middleware/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sample_drf_middleware/README.md b/sample_drf_middleware/README.md index c50cbbf..102d011 100644 --- a/sample_drf_middleware/README.md +++ b/sample_drf_middleware/README.md @@ -22,7 +22,7 @@ If you want to understand this more, check out the [basic drf authentication and - permissions.py - authentication.py -2. Override the import path in the files. +2. Override the hardcorded string in the files. ```python # permissions.py @@ -67,4 +67,4 @@ class UserListCreateAPIView(generics.ListCreateAPIView): queryset = User.objects.all() serializer_class = UserSerializer permission_classes=(IsAuthenticated,) -``` \ No newline at end of file +```