1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-14 22:33:17 +00:00

Added remote wipe option when unlinking a device.

This commit is contained in:
Shuai Lin
2016-05-07 11:45:47 +08:00
committed by lian
parent 7514c587d5
commit 7dc7034de4
10 changed files with 93 additions and 13 deletions

View File

@@ -7,7 +7,7 @@ from rest_framework.exceptions import APIException
import seaserv import seaserv
from seahub.base.accounts import User from seahub.base.accounts import User
from seahub.constants import GUEST_USER from seahub.constants import GUEST_USER
from seahub.api2.models import Token, TokenV2 from seahub.api2.models import Token, TokenV2, WipedDevice
from seahub.api2.utils import get_client_ip from seahub.api2.utils import get_client_ip
from seahub.utils import within_time_range from seahub.utils import within_time_range
try: try:
@@ -28,6 +28,8 @@ class AuthenticationFailed(APIException):
def __init__(self, detail=None): def __init__(self, detail=None):
self.detail = detail or self.default_detail self.detail = detail or self.default_detail
class DeviceRemoteWipedException(AuthenticationFailed):
pass
class TokenAuthentication(BaseAuthentication): class TokenAuthentication(BaseAuthentication):
""" """
@@ -98,7 +100,15 @@ class TokenAuthentication(BaseAuthentication):
try: try:
token = TokenV2.objects.get(key=key) token = TokenV2.objects.get(key=key)
except TokenV2.DoesNotExist: except TokenV2.DoesNotExist:
return None # Continue authentication in token v1 try:
token = WipedDevice.objects.get(key=key)
except WipedDevice.DoesNotExist:
pass
else:
raise DeviceRemoteWipedException('Device set to be remote wiped')
# Continue authentication in token v1
return None
try: try:
user = User.objects.get(email=token.user) user = User.objects.get(email=token.user)

25
seahub/api2/base.py Normal file
View File

@@ -0,0 +1,25 @@
#coding: UTF-8
from rest_framework.views import APIView as RestFrameworkAPIView
from seahub.api2.authentication import DeviceRemoteWipedException
class APIView(RestFrameworkAPIView):
"""
Subclass restframework's APIView to implement some custom feature like
adding a `X-Seafile-Wiped` header if the current client device has been
marked to be remote wiped by the user.
"""
def __init__(self, *a, **kw):
super(APIView, self).__init__(*a, **kw)
self._seafile_exc = None
def handle_exception(self, exc):
self._seafile_exc = exc
return super(APIView, self).handle_exception(exc)
def dispatch(self, *a, **kw):
response = super(APIView, self).dispatch(*a, **kw)
if self._seafile_exc and isinstance(self._seafile_exc, DeviceRemoteWipedException):
response['X-Seafile-Wiped'] = 'true'
return response

View File

@@ -1,7 +1,8 @@
import uuid import uuid
import hmac import hmac
import datetime
from hashlib import sha1 from hashlib import sha1
from django.db import models from django.db import models, transaction
from seahub.base.fields import LowerCaseCharField from seahub.base.fields import LowerCaseCharField
@@ -101,6 +102,14 @@ class TokenV2Manager(models.Manager):
def delete_device_token(self, username, platform, device_id): def delete_device_token(self, username, platform, device_id):
super(TokenV2Manager, self).filter(user=username, platform=platform, device_id=device_id).delete() super(TokenV2Manager, self).filter(user=username, platform=platform, device_id=device_id).delete()
def mark_device_to_be_remote_wiped(self, username, platform, device_id):
token = self._get_token_by_user_device(username, platform, device_id)
if not token:
return
with transaction.atomic():
wiped_device = WipedDevice(key=token.key)
wiped_device.save()
token.delete()
class TokenV2(models.Model): class TokenV2(models.Model):
""" """
@@ -162,3 +171,8 @@ class TokenV2(models.Model):
platform_version=self.platform_version, platform_version=self.platform_version,
last_accessed=self.last_accessed, last_accessed=self.last_accessed,
last_login_ip=self.last_login_ip) last_login_ip=self.last_login_ip)
class WipedDevice(models.Model):
key = models.CharField(max_length=40, primary_key=True)
wiped_at = models.DateTimeField(auto_now=True)

View File

@@ -23,6 +23,7 @@ urlpatterns = patterns('',
url(r'^server-info/$', ServerInfoView.as_view()), url(r'^server-info/$', ServerInfoView.as_view()),
url(r'^logout-device/$', LogoutDeviceView.as_view()), url(r'^logout-device/$', LogoutDeviceView.as_view()),
url(r'^client-login/$', ClientLoginTokenView.as_view()), url(r'^client-login/$', ClientLoginTokenView.as_view()),
url(r'^device-wiped/$', RemoteWipeReportView.as_view()),
# RESTful API # RESTful API
url(r'^accounts/$', Accounts.as_view(), name="accounts"), url(r'^accounts/$', Accounts.as_view(), name="accounts"),

View File

@@ -16,7 +16,6 @@ from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated, IsAdminUser from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView
from django.contrib.auth.hashers import check_password from django.contrib.auth.hashers import check_password
from django.contrib.sites.models import RequestSite from django.contrib.sites.models import RequestSite
@@ -40,6 +39,7 @@ from .utils import get_diff_details, \
api_repo_user_folder_perm_check, api_repo_setting_permission_check, \ api_repo_user_folder_perm_check, api_repo_setting_permission_check, \
api_repo_group_folder_perm_check api_repo_group_folder_perm_check
from seahub.api2.base import APIView
from seahub.avatar.templatetags.avatar_tags import api_avatar_url, avatar from seahub.avatar.templatetags.avatar_tags import api_avatar_url, avatar
from seahub.avatar.templatetags.group_avatar_tags import api_grp_avatar_url, \ from seahub.avatar.templatetags.group_avatar_tags import api_grp_avatar_url, \
grp_avatar grp_avatar
@@ -1914,6 +1914,7 @@ class DevicesView(APIView):
platform = request.data.get('platform', '') platform = request.data.get('platform', '')
device_id = request.data.get('device_id', '') device_id = request.data.get('device_id', '')
remote_wipe = request.data.get('wipe_device', '')
if not platform: if not platform:
error_msg = 'platform invalid.' error_msg = 'platform invalid.'
@@ -1924,7 +1925,10 @@ class DevicesView(APIView):
return api_error(status.HTTP_400_BAD_REQUEST, error_msg) return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try: try:
do_unlink_device(request.user.username, platform, device_id) do_unlink_device(request.user.username,
platform,
device_id,
remote_wipe=remote_wipe)
except SearpcError as e: except SearpcError as e:
logger.error(e) logger.error(e)
error_msg = 'Internal Server Error' error_msg = 'Internal Server Error'
@@ -4534,3 +4538,20 @@ class RepoGroupFolderPerm(APIView):
logger.error(e) logger.error(e)
error_msg = 'Internal Server Error' error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
class RemoteWipeReportView(APIView):
throttle_classes = (UserRateThrottle,)
@json_response
def post(self, request):
from seahub.api2.models import WipedDevice
token = request.POST.get('token', '')
if not token or len(token) != 40:
return api_error(status.HTTP_400_BAD_REQUEST, "device token is missing")
try:
entry = WipedDevice.objects.get(key=token)
entry.delete()
except WipedDevice.DoesNotExist:
return api_error(status.HTTP_400_BAD_REQUEST, "invalid device token")
return {}

View File

@@ -1,9 +1,9 @@
from rest_framework import status from rest_framework import status
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from seaserv import seafile_api from seaserv import seafile_api
from seahub import settings from seahub import settings
from seahub.api2.base import APIView
from seahub.api2.throttling import AnonRateThrottle, UserRateThrottle from seahub.api2.throttling import AnonRateThrottle, UserRateThrottle
from seahub.api2.utils import json_response, api_error from seahub.api2.utils import json_response, api_error
from seahub.api2.authentication import TokenAuthentication from seahub.api2.authentication import TokenAuthentication

View File

@@ -1,5 +1,4 @@
from rest_framework.views import APIView from seahub.api2.base import APIView
from seahub.api2.utils import json_response, is_seafile_pro from seahub.api2.utils import json_response, is_seafile_pro
from seahub import settings from seahub import settings
from seahub.utils import HAS_OFFICE_CONVERTER, HAS_FILE_SEARCH from seahub.utils import HAS_OFFICE_CONVERTER, HAS_FILE_SEARCH

View File

@@ -68,7 +68,7 @@ def get_user_synced_repo_infos(username):
return ret return ret
def do_unlink_device(username, platform, device_id): def do_unlink_device(username, platform, device_id, remote_wipe=False):
if platform in DESKTOP_PLATFORMS: if platform in DESKTOP_PLATFORMS:
# For desktop client, we also remove the sync tokens # For desktop client, we also remove the sync tokens
msg = 'failed to delete_repo_tokens_by_peer_id' msg = 'failed to delete_repo_tokens_by_peer_id'
@@ -80,4 +80,7 @@ def do_unlink_device(username, platform, device_id):
logger.exception(msg) logger.exception(msg)
raise raise
if remote_wipe:
TokenV2.objects.mark_device_to_be_remote_wiped(username, platform, device_id)
else:
TokenV2.objects.delete_device_token(username, platform, device_id) TokenV2.objects.delete_device_token(username, platform, device_id)

View File

@@ -11,6 +11,9 @@ define([
'platform': this.get('platform'), 'platform': this.get('platform'),
'device_id': this.get('device_id') 'device_id': this.get('device_id')
}; };
if (options.wipe_device) {
data['wipe_device'] = 'true';
}
$.ajax({ $.ajax({
url: Common.getUrl({name: 'devices'}), url: Common.getUrl({name: 'devices'}),

View File

@@ -73,9 +73,13 @@ define([
device_name = this.model.get('device_name'); device_name = this.model.get('device_name');
var title = gettext('Unlink device'); var title = gettext('Unlink device');
var content = gettext('Are you sure you want to unlink this device?'); var content = gettext('Are you sure you want to unlink this device?');
var extraOption = gettext('Delete files from this device the next time it comes online.');
var yesCallback = function () { var yesCallback = function (wipe_device) {
console.log('wipe_device = ' + wipe_device);
_this.model.unlink({ _this.model.unlink({
wipe_device: wipe_device,
success: function() { success: function() {
_this.remove(); _this.remove();
@@ -92,7 +96,7 @@ define([
}); });
return false; return false;
}; };
Common.showConfirm(title, content, yesCallback); Common.showConfirmWithExtraOption(title, content, extraOption, yesCallback);
} }
}); });