pull/530/head
ibuler 8 years ago
parent 92d854b971
commit 70da177ed7

@ -1,6 +1,7 @@
# ~*~ coding: utf-8 ~*~
from rest_framework import viewsets, generics, mixins
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin, ListBulkCreateUpdateDestroyAPIView
@ -90,34 +91,19 @@ class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
permission_classes = (IsSuperUser,)
class SystemUserAuthApi(APIView):
class SystemUserAuthInfoApi(generics.RetrieveAPIView):
queryset = SystemUser.objects.all()
permission_classes = (IsSuperUserOrAppUser,)
def get(self, request, *args, **kwargs):
system_user_id = request.query_params.get('system_user_id', -1)
system_user_username = request.query_params.get('system_user_username', '')
system_user = get_object_or_none(SystemUser, id=system_user_id, username=system_user_username)
if system_user:
if system_user.password:
password = signer.sign(system_user.password)
else:
password = signer.sign('')
if system_user.private_key:
private_key = signer.sign(system_user.private_key)
else:
private_key = signer.sign(None)
response = {
'id': system_user.id,
'password': password,
'private_key': private_key,
}
return Response(response)
else:
return Response({'msg': 'error system user id or username'}, status=401)
def retrieve(self, request, *args, **kwargs):
system_user = self.get_object()
data = {
'id': system_user.id,
'name': system_user.name,
'username': system_user.username,
'password': system_user.password,
'private_key': system_user.private_key,
'auth_method': system_user.auth_method,
}
return Response(data)

@ -263,7 +263,7 @@ class SystemUserForm(forms.ModelForm):
class Meta:
model = SystemUser
fields = [
'name', 'username', 'protocol', 'auto_generate_key', 'password', 'private_key_file', 'as_default',
'name', 'username', 'protocol', 'auto_generate_key', 'password', 'private_key_file', 'auth_method',
'auto_push', 'auto_update', 'sudo', 'comment', 'shell', 'home', 'uid',
]
widgets = {
@ -273,8 +273,8 @@ class SystemUserForm(forms.ModelForm):
help_texts = {
'name': '* required',
'username': '* required',
'auth_push': 'Auto push system user to asset',
'auth_update': 'Auto update system user ssh key',
'auto_push': 'Auto push system user to asset',
'auto_update': 'Auto update system user ssh key',
}

@ -95,13 +95,18 @@ class SystemUser(models.Model):
PROTOCOL_CHOICES = (
('ssh', 'ssh'),
)
AUTH_METHOD_CHOICES = (
('P', 'Password'),
('K', 'Public key'),
)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username'))
_password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
_private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
_public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
as_default = models.BooleanField(default=False, verbose_name=_('As default'))
auth_method = models.CharField(choices=AUTH_METHOD_CHOICES, default='K',
max_length=1, verbose_name=_('Auth method'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
auto_update = models.BooleanField(default=True, verbose_name=_('Auto update pass/key'))
sudo = models.TextField(max_length=4096, default='/user/bin/whoami', verbose_name=_('Sudo'))

@ -17,6 +17,7 @@ class AssetGroupSerializer(serializers.ModelSerializer):
def get_assets_amount(obj):
return obj.assets.count()
class AssetUpdateGroupSerializer(serializers.ModelSerializer):
groups = serializers.PrimaryKeyRelatedField(many=True, queryset=AssetGroup.objects.all())
@ -24,6 +25,7 @@ class AssetUpdateGroupSerializer(serializers.ModelSerializer):
model = Asset
fields = ['id', 'groups']
class AssetUpdateSystemUserSerializer(serializers.ModelSerializer):
system_users = serializers.PrimaryKeyRelatedField(many=True, queryset=SystemUser.objects.all())
@ -31,6 +33,7 @@ class AssetUpdateSystemUserSerializer(serializers.ModelSerializer):
model = Asset
fields = ['id', 'system_users']
class AdminUserSerializer(serializers.ModelSerializer):
class Meta:
model = AdminUser
@ -52,6 +55,12 @@ class SystemUserSerializer(serializers.ModelSerializer):
return fields
class SystemUserSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = SystemUser
fields = ('id', 'name', 'username')
class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
# system_users = SystemUserSerializer(many=True, read_only=True)
# admin_user = AdminUserSerializer(many=False, read_only=True)
@ -75,7 +84,7 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
class AssetGrantedSerializer(serializers.ModelSerializer):
system_users = SystemUserSerializer(many=True, read_only=True)
system_users = SystemUserSimpleSerializer(many=True, read_only=True)
is_inherited = serializers.SerializerMethodField()
system_users_join = serializers.SerializerMethodField()

@ -16,11 +16,12 @@ router.register(r'v1/system-user', api.SystemUserViewSet, 'system-user')
urlpatterns = [
url(r'^v1/assets_bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
# url(r'^v1/idc/(?P<pk>[0-9]+)/assets/$', api.IDCAssetsApi.as_view(), name='api-idc-assets'),
url(r'^v1/system-user/auth/', api.SystemUserAuthApi.as_view(), name='system-user-auth'),
url(r'^v1/system-user/(?P<pk>[0-9]+)/auth-info/', api.SystemUserAuthInfoApi.as_view(),
name='system-user-auth-info'),
url(r'^v1/assets/(?P<pk>\d+)/groups/$',
api.AssetUpdateGroupApi.as_view(), name='asset-update-group'),
url(r'^v1/assets/(?P<pk>\d+)/system-users/$',
api.SystemUserUpdateApi.as_view(), name='asset-update-systemusers'),
api.SystemUserUpdateApi.as_view(), name='asset-update-system-users'),
]
urlpatterns += router.urls

@ -18,7 +18,6 @@ class LoginLog(models.Model):
username = models.CharField(max_length=20, verbose_name=_('Username'))
name = models.CharField(max_length=20, blank=True, verbose_name=_('Name'))
login_type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2, verbose_name=_('Login type'))
terminal = models.CharField(max_length=32, verbose_name=_('Terminal'))
login_ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
login_city = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('Login city'))
user_agent = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('User agent'))

@ -17,25 +17,28 @@ def validate_ip(ip):
return False
def write_login_log(username, name='', login_type='W',
terminal='', login_ip='', user_agent=''):
def write_login_log(username, name='', login_type='',
login_ip='', user_agent=''):
if not (login_ip and validate_ip(login_ip)):
login_ip = '0.0.0.0'
if not name:
name = username
login_city = get_ip_city(login_ip)
LoginLog.objects.create(username=username, name=name, login_type=login_type, login_ip=login_ip,
terminal=terminal, login_city=login_city, user_agent=user_agent)
LoginLog.objects.create(username=username, name=name, login_type=login_type,
login_ip=login_ip, login_city=login_city, user_agent=user_agent)
def get_ip_city(ip, timeout=10):
# Taobao ip api: http://ip.taobao.com//service/getIpInfo.php?ip=8.8.8.8
# Sina ip api: http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=8.8.8.8&format=js
# Sina ip api: http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=8.8.8.8&format=json
url = 'http://ip.taobao.com/service/getIpInfo.php?ip=' + ip
r = requests.get(url, timeout=timeout)
url = 'http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=%s&format=json' % ip
try:
r = requests.get(url, timeout=timeout)
except requests.Timeout:
r = None
city = 'Unknown'
if r.status_code == 200:
if r and r.status_code == 200:
try:
data = r.json()
if data['code'] == 0:

@ -100,7 +100,7 @@ class UserToken(APIView):
user, msg = check_user_valid(username=username, email=email,
password=password, public_key=public_key)
if user:
token = generate_token(request)
token = generate_token(request, user)
return Response({'Token': token, 'key': 'Bearer'}, status=200)
else:
return Response({'error': msg}, status=406)
@ -114,28 +114,22 @@ class UserProfile(APIView):
class UserAuthApi(APIView):
permission_classes = ()
expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600
permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs):
username = request.data.get('username', '')
password = request.data.get('password', '')
public_key = request.data.get('public_key', '')
remote_addr = request.data.get('remote_addr', '')
terminal = request.data.get('applications', '')
login_type = request.data.get('login_type', 'T')
user = check_user_valid(username=username, password=password, public_key=public_key)
login_type = request.data.get('login_type', '')
login_ip = request.META.get('REMOTE_ADDR', '')
user_agent = request.data.get('HTTP_USER_AGENT', '')
user, msg = check_user_valid(username=username, password=password, public_key=public_key)
if user:
token = cache.get('%s_%s' % (user.id, remote_addr))
if not token:
token = generate_token(request)
cache.set(token, user.id, self.expiration)
cache.set('%s_%s' % (user.id, remote_addr), token, self.expiration)
write_login_log_async.delay(user.username, name=user.name, terminal=terminal,
login_ip=remote_addr, login_type=login_type)
return Response({'token': token, 'id': user.id, 'username': user.username,
'name': user.name, 'is_active': user.is_active})
token = generate_token(request, user)
write_login_log_async.delay(user.username, name=user.name, user_agent=user_agent,
login_ip=login_ip, login_type=login_type)
return Response({'token': token, 'user': user.to_json()})
else:
return Response({'msg': 'Invalid password or public key or user is not active or expired'}, status=401)
return Response({'msg': msg}, status=401)

@ -43,7 +43,6 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
msg = _('Invalid signature header. Signature string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
try:
sign = auth[1].decode().split(':')
if len(sign) != 2:
@ -58,7 +57,8 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
return self.authenticate_credentials(request, access_key_id, request_signature)
def authenticate_credentials(self, request, access_key_id, request_signature):
@staticmethod
def authenticate_credentials(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:
@ -109,7 +109,8 @@ class AccessTokenAuthentication(authentication.BaseAuthentication):
raise exceptions.AuthenticationFailed(msg)
return self.authenticate_credentials(token)
def authenticate_credentials(self, token):
@staticmethod
def authenticate_credentials(token):
user_id = cache.get(token)
user = get_object_or_none(User, id=user_id)

@ -17,6 +17,7 @@ router.register(r'v1/user-groups', api.UserGroupViewSet, 'user-group')
urlpatterns = [
url(r'^v1/token/$', api.UserToken.as_view(), name='user-token'),
url(r'^v1/profile/$', api.UserProfile.as_view(), name='user-profile'),
url(r'^v1/auth/$', api.UserAuthApi.as_view(), name='user-auth'),
url(r'^v1/users/(?P<pk>\d+)/password/reset/$', api.UserResetPasswordApi.as_view(), name='user-reset-password'),
url(r'^v1/users/(?P<pk>\d+)/public-key/reset/$', api.UserResetPKApi.as_view(), name='user-public-key-reset'),
url(r'^v1/users/(?P<pk>\d+)/public-key/update/$', api.UserUpdatePKApi.as_view(), name='user-public-key-update'),

@ -180,8 +180,8 @@ def send_reset_ssh_key_mail(user):
def check_user_valid(**kwargs):
password = kwargs.pop('password', None)
public_key = kwargs.pop('public_key', None)
email = kwargs.pop('email')
username = kwargs.pop('username')
email = kwargs.pop('email', None)
username = kwargs.pop('username', None)
if username:
user = get_object_or_none(User, username=username)
@ -206,24 +206,23 @@ def check_user_valid(**kwargs):
elif len(public_key_saved) > 1:
if public_key == public_key_saved[1]:
return user, ''
return None, _('Passowrd or SSH public key invalid')
return None, _('Password or SSH public key invalid')
def refresh_token(token, user):
expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600
def refresh_token(token, user, expiration=3600):
cache.set(token, user.id, expiration)
def generate_token(request):
def generate_token(request, user):
expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600
remote_addr = request.META.get('REMOTE_ADDR', '')
remote_addr = base64.b16encode(remote_addr).replace('=', '')
token = cache.get('%s_%s' % (request.user.id, remote_addr))
token = cache.get('%s_%s' % (user.id, remote_addr))
if not token:
token = uuid.uuid4().get_hex()
print('Set cache: %s' % token)
cache.set(token, request.user.id, expiration)
cache.set('%s_%s' % (request.user.id, remote_addr), token, expiration)
cache.set(token, user.id, expiration)
cache.set('%s_%s' % (user.id, remote_addr), token, expiration)
return token

Loading…
Cancel
Save