Commit 27070123 authored by ibuler's avatar ibuler

Finish access key auth

parent c5ab49c5
......@@ -8,7 +8,7 @@ from django.shortcuts import get_object_or_404
from common.mixins import IDInFilterMixin
from common.utils import get_object_or_none, signer
from .hands import IsSuperUserOrTerminalUser, IsSuperUser
from .hands import IsSuperUserOrAppUser, IsSuperUser
from .models import AssetGroup, Asset, IDC, SystemUser, AdminUser
from . import serializers
......@@ -18,6 +18,7 @@ class AssetViewSet(IDInFilterMixin, viewsets.ModelViewSet):
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
filter_fields = ('id', 'ip', 'hostname')
permission_classes = (IsSuperUserOrAppUser,)
def get_queryset(self):
queryset = super(AssetViewSet, self).get_queryset()
......@@ -90,7 +91,7 @@ class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
class SystemUserAuthApi(APIView):
permission_classes = (IsSuperUserOrTerminalUser,)
permission_classes = (IsSuperUserOrAppUser,)
def get(self, request, *args, **kwargs):
system_user_id = request.query_params.get('system_user_id', -1)
......
......@@ -12,5 +12,5 @@
from users.utils import AdminUserRequiredMixin
from users.permissions import IsSuperUserOrTerminalUser, IsSuperUser
from users.permissions import IsSuperUserOrAppUser, IsSuperUser
from users.models import User, UserGroup
......@@ -7,7 +7,7 @@ from rest_framework import generics, viewsets
from rest_framework.views import APIView, Response
from . import models, serializers
from .hands import IsSuperUserOrTerminalUser, Terminal
from .hands import IsSuperUserOrAppUser, Terminal
class ProxyLogViewSet(viewsets.ModelViewSet):
......@@ -32,13 +32,13 @@ class ProxyLogViewSet(viewsets.ModelViewSet):
queryset = models.ProxyLog.objects.all()
serializer_class = serializers.ProxyLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
permission_classes = (IsSuperUserOrAppUser,)
class CommandLogViewSet(viewsets.ModelViewSet):
queryset = models.CommandLog.objects.all()
serializer_class = serializers.CommandLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
permission_classes = (IsSuperUserOrAppUser,)
# class CommandLogTitleApi(APIView):
......
......@@ -4,5 +4,5 @@
from users.utils import AdminUserRequiredMixin
from users.models import User
from assets.models import Asset, SystemUser
from users.permissions import IsSuperUserOrTerminalUser
from users.permissions import IsSuperUserOrAppUser
from terminal.models import Terminal
......@@ -2,6 +2,81 @@
# -*- coding: utf-8 -*-
#
"""
兼容Python版本
"""
import sys
is_py2 = (sys.version_info[0] == 2)
is_py3 = (sys.version_info[0] == 3)
try:
import simplejson as json
except (ImportError, SyntaxError):
import json
if is_py2:
def to_bytes(data):
"""若输入为unicode, 则转为utf-8编码的bytes;其他则原样返回。"""
if isinstance(data, unicode):
return data.encode('utf-8')
else:
return data
def to_string(data):
"""把输入转换为str对象"""
return to_bytes(data)
def to_unicode(data):
"""把输入转换为unicode,要求输入是unicode或者utf-8编码的bytes。"""
if isinstance(data, bytes):
return data.decode('utf-8')
else:
return data
def stringify(input):
if isinstance(input, dict):
return dict([(stringify(key), stringify(value)) for key,value in input.iteritems()])
elif isinstance(input, list):
return [stringify(element) for element in input]
elif isinstance(input, unicode):
return input.encode('utf-8')
else:
return input
builtin_str = str
bytes = str
str = unicode
elif is_py3:
def to_bytes(data):
"""若输入为str(即unicode),则转为utf-8编码的bytes;其他则原样返回"""
if isinstance(data, str):
return data.encode(encoding='utf-8')
else:
return data
def to_string(data):
"""若输入为bytes,则认为是utf-8编码,并返回str"""
if isinstance(data, bytes):
return data.decode('utf-8')
else:
return data
def to_unicode(data):
"""把输入转换为unicode,要求输入是unicode或者utf-8编码的bytes。"""
return to_string(data)
def stringify(input):
return input
builtin_str = str
bytes = bytes
str = str
if __name__ == '__main__':
pass
......@@ -3,12 +3,17 @@
from __future__ import unicode_literals
from six import string_types
import base64
import os
from itertools import chain
import string
import logging
import datetime
import paramiko
import time
import hashlib
from email.utils import formatdate
import calendar
import threading
import paramiko
import sshpubkeys
......@@ -16,7 +21,6 @@ from itsdangerous import TimedJSONWebSignatureSerializer, JSONWebSignatureSerial
BadSignature, SignatureExpired
from django.shortcuts import reverse as dj_reverse
from django.conf import settings
from django.core import signing
from django.utils import timezone
try:
......@@ -24,6 +28,8 @@ try:
except ImportError:
import StringIO
from .compat import to_bytes, to_string
SECRET_KEY = settings.SECRET_KEY
......@@ -255,4 +261,63 @@ def setattr_bulk(seq, key, value):
return map(set_attr, seq)
def content_md5(data):
"""计算data的MD5值,经过Base64编码并返回str类型。
返回值可以直接作为HTTP Content-Type头部的值
"""
m = hashlib.md5(to_bytes(data))
return to_string(base64.b64encode(m.digest()))
_STRPTIME_LOCK = threading.Lock()
_GMT_FORMAT = "%a, %d %b %Y %H:%M:%S GMT"
_ISO8601_FORMAT = "%Y-%m-%dT%H:%M:%S.000Z"
def to_unixtime(time_string, format_string):
with _STRPTIME_LOCK:
return int(calendar.timegm(time.strptime(time_string, format_string)))
def http_date(timeval=None):
"""返回符合HTTP标准的GMT时间字符串,用strftime的格式表示就是"%a, %d %b %Y %H:%M:%S GMT"。
但不能使用strftime,因为strftime的结果是和locale相关的。
"""
return formatdate(timeval, usegmt=True)
def http_to_unixtime(time_string):
"""把HTTP Date格式的字符串转换为UNIX时间(自1970年1月1日UTC零点的秒数)。
HTTP Date形如 `Sat, 05 Dec 2015 11:10:29 GMT` 。
"""
return to_unixtime(time_string, _GMT_FORMAT)
def iso8601_to_unixtime(time_string):
"""把ISO8601时间字符串(形如,2012-02-24T06:07:48.000Z)转换为UNIX时间,精确到秒。"""
return to_unixtime(time_string, _ISO8601_FORMAT)
def http_to_unixtime(time_string):
"""把HTTP Date格式的字符串转换为UNIX时间(自1970年1月1日UTC零点的秒数)。
HTTP Date形如 `Sat, 05 Dec 2015 11:10:29 GMT` 。
"""
return to_unixtime(time_string, "%a, %d %b %Y %H:%M:%S GMT")
def make_signature(access_key_secret, date=None):
if isinstance(date, int):
date_gmt = http_date(date)
elif date is None:
date_gmt = http_date(int(time.time()))
else:
date_gmt = date
data = str(access_key_secret) + "\n" + date_gmt
return content_md5(data)
signer = Signer()
\ No newline at end of file
......@@ -272,13 +272,12 @@ REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': (
'users.permissions.IsValidUser',
'users.permissions.IsSuperUser',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'users.authentication.TerminalAuthentication',
'users.authentication.AccessKeyAuthentication',
'users.authentication.AccessTokenAuthentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
......
......@@ -11,7 +11,7 @@ from rest_framework.permissions import AllowAny
from common.utils import signer, get_object_or_none
from .models import Terminal, TerminalHeatbeat
from .serializers import TerminalSerializer, TerminalHeatbeatSerializer
from .hands import IsSuperUserOrTerminalUser, User
from .hands import IsSuperUserOrAppUser, User
class TerminalRegister(ListCreateAPIView):
......@@ -62,13 +62,13 @@ class TerminalViewSet(viewsets.ModelViewSet):
class TerminalHeatbeatApi(ListCreateAPIView):
queryset = TerminalHeatbeat.objects.all()
serializer_class = TerminalHeatbeatSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
permission_classes = (IsSuperUserOrAppUser,)
class TerminalHeatbeatViewSet(viewsets.ModelViewSet):
queryset = TerminalHeatbeat.objects.all()
serializer_class = TerminalHeatbeatSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
permission_classes = (IsSuperUserOrAppUser,)
def create(self, request, *args, **kwargs):
terminal = request.user
......
......@@ -2,5 +2,5 @@
#
from users.models import User
from users.permissions import IsSuperUserOrTerminalUser
from audits.models import ProxyLog
from users.permissions import IsSuperUserOrAppUser
from audits.models import ProxyLog
\ No newline at end of file
......@@ -16,7 +16,7 @@ from common.utils import get_logger
from .utils import check_user_valid, get_or_refresh_token
from .models import User, UserGroup
from .hands import write_login_log_async
from .permissions import IsSuperUser, IsTerminalUser, IsValidUser, IsSuperUserOrTerminalUser
from .permissions import IsSuperUser, IsAppUser, IsValidUser, IsSuperUserOrAppUser
from . import serializers
......
......@@ -2,6 +2,8 @@
#
import base64
import hashlib
import time
from django.core.cache import cache
from django.conf import settings
......@@ -12,7 +14,7 @@ from django.utils.six import text_type
from django.utils.translation import ugettext_lazy as _
from rest_framework import HTTP_HEADER_ENCODING
from common.utils import get_object_or_none
from common.utils import get_object_or_none, make_signature, http_to_unixtime
from .utils import get_or_refresh_token
from .models import User, AccessKey
......@@ -22,7 +24,6 @@ def get_request_date_header(request):
if isinstance(date, text_type):
# Work around django test client oddness
date = date.encode(HTTP_HEADER_ENCODING)
return date
......@@ -54,18 +55,30 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
raise exceptions.AuthenticationFailed(msg)
access_key_id = sign[0]
secret = sign[1]
date =
return self.authenticate_credentials(sign)
request_signature = sign[1]
def authenticate_credentials(self, access_key_id, secret, datetime):
access_key_id = sign[0]
secret = sign[1]
return self.authenticate_credentials(request, access_key_id, request_signature)
def authenticate_credentials(self, request, access_key_id, request_signature):
access_key = get_object_or_none(AccessKey, id=access_key_id)
request_date = get_request_date_header(request)
if access_key is None or not access_key.user:
raise exceptions.AuthenticationFailed(_('Invalid signature.'))
access_key_secret = access_key.secret
print(request_date)
try:
request_unix_time = http_to_unixtime(request_date)
except ValueError:
raise exceptions.AuthenticationFailed(_('HTTP header: Date not provide or not %a, %d %b %Y %H:%M:%S GMT'))
if int(time.time()) - request_unix_time > 15*60:
raise exceptions.AuthenticationFailed(_('Expired, more than 15 minutes'))
signature = make_signature(access_key_secret, request_date)
if not signature == request_signature:
raise exceptions.AuthenticationFailed(_('Invalid signature. %s: %s' % (signature, request_signature)))
if not access_key.user.is_active:
raise exceptions.AuthenticationFailed(_('User disabled.'))
......
......@@ -101,8 +101,8 @@ class User(AbstractUser):
return False
@property
def is_terminal(self):
return False
def is_app(self):
return self.role == 'App'
@is_superuser.setter
def is_superuser(self, value):
......
......@@ -23,12 +23,12 @@ class IsValidUser(permissions.IsAuthenticated, permissions.BasePermission):
and request.user.is_valid
class IsTerminalUser(IsValidUser, permissions.BasePermission):
class IsAppUser(IsValidUser, permissions.BasePermission):
"""Allows access only to app user """
def has_permission(self, request, view):
return super(IsTerminalUser, self).has_permission(request, view) \
and isinstance(request.user, Terminal)
return super(IsAppUser, self).has_permission(request, view) \
and request.user.is_app()
class IsSuperUser(IsValidUser, permissions.BasePermission):
......@@ -39,12 +39,12 @@ class IsSuperUser(IsValidUser, permissions.BasePermission):
and request.user.is_superuser
class IsSuperUserOrTerminalUser(IsValidUser, permissions.BasePermission):
class IsSuperUserOrAppUser(IsValidUser, permissions.BasePermission):
"""Allows access between superuser and app user"""
def has_permission(self, request, view):
return super(IsSuperUserOrTerminalUser, self).has_permission(request, view) \
and (request.user.is_superuser or request.user.is_terminal)
return super(IsSuperUserOrAppUser, self).has_permission(request, view) \
and (request.user.is_superuser or request.user.is_app)
if __name__ == '__main__':
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment