Skip to content

Commit

Permalink
Merge pull request #495 from zhuyuanmao/refine-auth
Browse files Browse the repository at this point in the history
Deprecate out-maintained auth dependency
  • Loading branch information
yeasy authored Nov 12, 2022
2 parents fc03dab + 30eb379 commit 828c588
Show file tree
Hide file tree
Showing 15 changed files with 128 additions and 214 deletions.
67 changes: 0 additions & 67 deletions src/agent/k8s-rest-agent/src/api/auth.py
Original file line number Diff line number Diff line change
@@ -1,67 +0,0 @@
import base64
import json
import logging

from django.contrib.auth import get_user_model
from django.utils.translation import ugettext as _
from rest_framework import authentication
from rest_framework import exceptions
from rest_framework_jwt.authentication import (
JSONWebTokenAuthentication as CoreJSONWebTokenAuthentication,
)
from rest_framework_jwt.settings import api_settings

jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER
User = get_user_model()

LOG = logging.getLogger(__name__)


class JSONWebTokenAuthentication(CoreJSONWebTokenAuthentication):
@staticmethod
def _get_or_create_user(user_id, payload=None):
if payload is None:
payload = {}

user, _ = User.objects.get_or_create(
id=user_id, username=user_id, defaults={"password": user_id}
)

return user

def authenticate_credentials(self, payload):
"""
Returns an active user that matches the payload's user id and email.
"""
username = jwt_get_username_from_payload(payload)

if not username:
msg = _("Invalid payload.")
raise exceptions.AuthenticationFailed(msg)

user = self._get_or_create_user(username, payload)

if not user.is_active:
msg = _("User account is disabled.")
raise exceptions.AuthenticationFailed(msg)

return user


class IstioJWTAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
token = request.META.get("HTTP_TOKEN", None)
if token is None:
return None

token += "=" * (-len(token) % 4)
token = base64.b64decode(token)
token = json.loads(token)
user_id = token.get("sub", None)
if user_id is None:
return None
user, _ = User.objects.get_or_create(
id=user_id, username=user_id, defaults={"password": user_id}
)
return user, None
5 changes: 1 addition & 4 deletions src/agent/k8s-rest-agent/src/api/routes/hello/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,16 @@

from rest_framework import viewsets, status
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import action
from rest_framework.response import Response

from api.auth import JSONWebTokenAuthentication, IstioJWTAuthentication
from api.utils.mixins import PermissionsPerMethodMixin

LOG = logging.getLogger(__name__)
APP_VERSION = os.getenv("APP_VERSION", "v1")


class HelloViewSet(PermissionsPerMethodMixin, viewsets.ViewSet):
authentication_classes = (IstioJWTAuthentication,)

@swagger_auto_schema(
operation_summary="Hello world", operation_description="Hello world"
Expand Down
41 changes: 0 additions & 41 deletions src/api-engine/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import authentication
from rest_framework.permissions import BasePermission
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer
from api.common.enums import UserRole
from api.models import UserProfile

Expand All @@ -19,45 +17,6 @@
ADMIN_NAME = os.getenv("ADMIN_USERNAME")


class CustomAuthenticate(authentication.BaseAuthentication):
def authenticate(self, request):
authorization = request.META.get("HTTP_AUTHORIZATION", None)
if not authorization or not authorization.startswith("JWT"):
return None
token = authorization.split(" ")[-1]
if token == SUPER_USER_TOKEN:
username = ADMIN_NAME
try:
user = UserProfile.objects.get(username=username)
except ObjectDoesNotExist:
return None

return user, None
else:
return None


class TokenAuth(authentication.BaseAuthentication):

def authenticate(self, request):
try:
token = request.META.get('HTTP_AUTHORIZATION', None).split()[1]
token = {"token": token}

valid_data = VerifyJSONWebTokenSerializer().validate(token)
user = valid_data['user']
# organization = user.organization
# organization_id = user.organization.id
# organization_name = user.organization.name
# request.user.
if user:
return
else:
raise AuthenticationFailed('认证失败')
except Exception:
raise AuthenticationFailed('认证失败')


class IsAdminAuthenticated(BasePermission):
"""
Allows access only to authenticated users.
Expand Down
21 changes: 3 additions & 18 deletions src/api-engine/api/routes/agent/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,9 @@
from drf_yasg.utils import swagger_auto_schema
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

from api.auth import IsOperatorAuthenticated, IsAdminAuthenticated
from api.common.enums import HostType
from api.exceptions import (
ResourceNotFound,
Expand All @@ -22,7 +19,7 @@
NoResource,
ResourceInUse,
)
from api.models import Agent, KubernetesConfig, Organization
from api.models import Agent, KubernetesConfig
from api.routes.agent.serializers import (
AgentQuery,
AgentListResponse,
Expand All @@ -33,27 +30,15 @@
AgentInfoSerializer,
AgentApplySerializer,
)
from api.utils.common import with_common_response, any_of
from api.auth import TokenAuth
from api.utils.common import with_common_response
from api.common import ok, err

LOG = logging.getLogger(__name__)


class AgentViewSet(viewsets.ViewSet):
"""Class represents agent related operations."""
authentication_classes = (JSONWebTokenAuthentication, TokenAuth)

# def get_permissions(self):
# if self.action in ["apply", "list", "release", "retrieve"]:
# permission_classes = (
# IsAuthenticated,
# any_of(IsAdminAuthenticated, IsOperatorAuthenticated),
# )
# else:
# permission_classes = (IsAuthenticated, IsOperatorAuthenticated)
#
# return [permission() for permission in permission_classes]
permission_classes = [IsAuthenticated, ]

@swagger_auto_schema(
query_serializer=AgentQuery,
Expand Down
39 changes: 24 additions & 15 deletions src/api-engine/api/routes/chaincode/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.decorators import action
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
import os
import zipfile

Expand All @@ -20,7 +20,6 @@

from api.lib.peer.chaincode import ChainCode as PeerChainCode
from api.common.serializers import PageQuerySerializer
from api.auth import TokenAuth
from api.utils.common import with_common_response
from api.exceptions import ResourceNotFound

Expand All @@ -36,7 +35,7 @@

class ChainCodeViewSet(viewsets.ViewSet):
"""Class represents Channel related operations."""
authentication_classes = (JSONWebTokenAuthentication, TokenAuth)
permission_classes = [IsAuthenticated, ]

@swagger_auto_schema(
query_serializer=PageQuerySerializer,
Expand Down Expand Up @@ -74,7 +73,8 @@ def list(self, request):
}
for chaincode in chaincodes_pages
]
response = ChaincodeListResponse({"data": chanincodes_list, "total": chaincodes.count()})
response = ChaincodeListResponse(
{"data": chanincodes_list, "total": chaincodes.count()})
return Response(data=ok(response.data), status=status.HTTP_200_OK)
except Exception as e:
return Response(
Expand Down Expand Up @@ -143,7 +143,8 @@ def package(self, request):
envs = init_env_vars(peer_node, org)

peer_channel_cli = PeerChainCode("v2.2.0", **envs)
res = peer_channel_cli.lifecycle_package(name, version, chaincode_path, language)
res = peer_channel_cli.lifecycle_package(
name, version, chaincode_path, language)
os.system("rm -rf {}/*".format(file_path))
os.system("mv {}.tar.gz {}".format(name, file_path))
if res != 0:
Expand All @@ -162,8 +163,8 @@ def package(self, request):
err(e.args), status=status.HTTP_400_BAD_REQUEST
)
return Response(
ok("success"), status=status.HTTP_200_OK
)
ok("success"), status=status.HTTP_200_OK
)

@swagger_auto_schema(
method="post",
Expand Down Expand Up @@ -218,7 +219,8 @@ def query_installed(self, request):

timeout = "5s"
peer_channel_cli = PeerChainCode("v2.2.0", **envs)
res, installed_chaincodes = peer_channel_cli.lifecycle_query_installed(timeout)
res, installed_chaincodes = peer_channel_cli.lifecycle_query_installed(
timeout)
if res != 0:
return Response(err("query installed chaincode failed."), status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
Expand Down Expand Up @@ -272,7 +274,8 @@ def approve_for_my_org(self, request):
try:
channel_name = serializer.validated_data.get("channel_name")
chaincode_name = serializer.validated_data.get("chaincode_name")
chaincode_version = serializer.validated_data.get("chaincode_version")
chaincode_version = serializer.validated_data.get(
"chaincode_version")
policy = serializer.validated_data.get("policy")
# Perhaps the orderer's port is best stored in the database
orderer_url = serializer.validated_data.get("orderer_url")
Expand Down Expand Up @@ -330,7 +333,8 @@ def query_approved(self, request):
cc_name = request.data.get("chaincode_name")

peer_channel_cli = PeerChainCode("v2.2.0", **envs)
code, content = peer_channel_cli.lifecycle_query_approved(channel_name, cc_name)
code, content = peer_channel_cli.lifecycle_query_approved(
channel_name, cc_name)
if code != 0:
return Response(err("query_approved failed."), status=status.HTTP_400_BAD_REQUEST)

Expand All @@ -355,7 +359,8 @@ def check_commit_readiness(self, request):
try:
channel_name = serializer.validated_data.get("channel_name")
chaincode_name = serializer.validated_data.get("chaincode_name")
chaincode_version = serializer.validated_data.get("chaincode_version")
chaincode_version = serializer.validated_data.get(
"chaincode_version")
policy = serializer.validated_data.get("policy")
# Perhaps the orderer's port is best stored in the database
orderer_url = serializer.validated_data.get("orderer_url")
Expand Down Expand Up @@ -409,7 +414,8 @@ def commit(self, request):
try:
channel_name = serializer.validated_data.get("channel_name")
chaincode_name = serializer.validated_data.get("chaincode_name")
chaincode_version = serializer.validated_data.get("chaincode_version")
chaincode_version = serializer.validated_data.get(
"chaincode_version")
policy = serializer.validated_data.get("policy")
# Perhaps the orderer's port is best stored in the database
orderer_url = serializer.validated_data.get("orderer_url")
Expand Down Expand Up @@ -444,7 +450,8 @@ def commit(self, request):
print(peer_node.port)
# port = peer_node.port.all()[0].internal
# port = ports[0].internal
peer_address = peer_node.name + "." + org.name + ":" + str(7051)
peer_address = peer_node.name + \
"." + org.name + ":" + str(7051)
peer_address_list.append(peer_address)
peer_root_certs.append(peer_tls_cert)

Expand Down Expand Up @@ -481,7 +488,8 @@ def query_committed(self, request):
peer_node = qs.first()
envs = init_env_vars(peer_node, org)
peer_channel_cli = PeerChainCode("v2.2.0", **envs)
code, chaincodes_commited = peer_channel_cli.lifecycle_query_committed(channel_name, chaincode_name)
code, chaincodes_commited = peer_channel_cli.lifecycle_query_committed(
channel_name, chaincode_name)
if code != 0:
return Response(err("query committed failed."), status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
Expand Down Expand Up @@ -509,7 +517,8 @@ def init_env_vars(node, org):

envs = {
"CORE_PEER_TLS_ENABLED": "true",
"CORE_PEER_LOCALMSPID": "{}MSP".format(org_name.capitalize()), # "Org1.cello.comMSP"
# "Org1.cello.comMSP"
"CORE_PEER_LOCALMSPID": "{}MSP".format(org_name.capitalize()),
"CORE_PEER_TLS_ROOTCERT_FILE": "{}/{}/peers/{}/tls/ca.crt".format(dir_node, org_name, node.name + "." + org_name),
"CORE_PEER_ADDRESS": "{}:{}".format(
node.name + "." + org_name, str(7051)),
Expand Down
5 changes: 2 additions & 3 deletions src/api-engine/api/routes/channel/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
from rest_framework.permissions import IsAuthenticated
#

from drf_yasg.utils import swagger_auto_schema
Expand All @@ -20,7 +20,6 @@
from api.config import CELLO_HOME
from api.common.serializers import PageQuerySerializer
from api.utils.common import with_common_response, parse_block_file, to_dict
from api.auth import TokenAuth
from api.lib.configtxgen import ConfigTX, ConfigTxGen
from api.lib.peer.channel import Channel as PeerChannel
from api.lib.configtxlator.configtxlator import ConfigTxLator
Expand Down Expand Up @@ -61,7 +60,7 @@

class ChannelViewSet(viewsets.ViewSet):
"""Class represents Channel related operations."""
authentication_classes = (JSONWebTokenAuthentication, TokenAuth)
permission_classes = [IsAuthenticated, ]
parser_classes = [MultiPartParser, FormParser, JSONParser]

@ swagger_auto_schema(
Expand Down
2 changes: 0 additions & 2 deletions src/api-engine/api/routes/file/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from rest_framework import viewsets, status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

from api.auth import IsOperatorAuthenticated, IsAdminAuthenticated
from api.exceptions import CustomError
Expand All @@ -25,7 +24,6 @@


class FileViewSet(viewsets.ViewSet):
authentication_classes = (JSONWebTokenAuthentication,)
permission_classes = (
IsAuthenticated,
any_of(IsAdminAuthenticated, IsOperatorAuthenticated),
Expand Down
11 changes: 8 additions & 3 deletions src/api-engine/api/routes/general/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#
# SPDX-License-Identifier: Apache-2.0
#

from api.routes.user.serializers import UserInfoSerializer
from rest_framework import serializers


Expand All @@ -23,6 +24,10 @@ class RegisterResponse(serializers.Serializer):


class LoginBody(serializers.Serializer):
orgName = serializers.CharField(help_text="name of Organization")
username = serializers.CharField(help_text="name of user")
email = serializers.CharField(help_text="email of user")
password = serializers.CharField(help_text="password of user")


class LoginSuccessBody(serializers.Serializer):
token = serializers.CharField(help_text="access token")
user = UserInfoSerializer()
Loading

0 comments on commit 828c588

Please sign in to comment.