mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-14 14:21:23 +00:00
Added remote wipe option when unlinking a device.
This commit is contained in:
@@ -7,7 +7,7 @@ from rest_framework.exceptions import APIException
|
||||
import seaserv
|
||||
from seahub.base.accounts import 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.utils import within_time_range
|
||||
try:
|
||||
@@ -28,6 +28,8 @@ class AuthenticationFailed(APIException):
|
||||
def __init__(self, detail=None):
|
||||
self.detail = detail or self.default_detail
|
||||
|
||||
class DeviceRemoteWipedException(AuthenticationFailed):
|
||||
pass
|
||||
|
||||
class TokenAuthentication(BaseAuthentication):
|
||||
"""
|
||||
@@ -98,7 +100,15 @@ class TokenAuthentication(BaseAuthentication):
|
||||
try:
|
||||
token = TokenV2.objects.get(key=key)
|
||||
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:
|
||||
user = User.objects.get(email=token.user)
|
||||
|
25
seahub/api2/base.py
Normal file
25
seahub/api2/base.py
Normal 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
|
@@ -1,7 +1,8 @@
|
||||
import uuid
|
||||
import hmac
|
||||
import datetime
|
||||
from hashlib import sha1
|
||||
from django.db import models
|
||||
from django.db import models, transaction
|
||||
|
||||
from seahub.base.fields import LowerCaseCharField
|
||||
|
||||
@@ -101,6 +102,14 @@ class TokenV2Manager(models.Manager):
|
||||
def delete_device_token(self, username, platform, device_id):
|
||||
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):
|
||||
"""
|
||||
@@ -162,3 +171,8 @@ class TokenV2(models.Model):
|
||||
platform_version=self.platform_version,
|
||||
last_accessed=self.last_accessed,
|
||||
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)
|
||||
|
@@ -23,6 +23,7 @@ urlpatterns = patterns('',
|
||||
url(r'^server-info/$', ServerInfoView.as_view()),
|
||||
url(r'^logout-device/$', LogoutDeviceView.as_view()),
|
||||
url(r'^client-login/$', ClientLoginTokenView.as_view()),
|
||||
url(r'^device-wiped/$', RemoteWipeReportView.as_view()),
|
||||
|
||||
# RESTful API
|
||||
url(r'^accounts/$', Accounts.as_view(), name="accounts"),
|
||||
|
@@ -16,7 +16,6 @@ from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.permissions import IsAuthenticated, IsAdminUser
|
||||
from rest_framework.reverse import reverse
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from django.contrib.auth.hashers import check_password
|
||||
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_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.group_avatar_tags import api_grp_avatar_url, \
|
||||
grp_avatar
|
||||
@@ -1914,6 +1914,7 @@ class DevicesView(APIView):
|
||||
|
||||
platform = request.data.get('platform', '')
|
||||
device_id = request.data.get('device_id', '')
|
||||
remote_wipe = request.data.get('wipe_device', '')
|
||||
|
||||
if not platform:
|
||||
error_msg = 'platform invalid.'
|
||||
@@ -1924,7 +1925,10 @@ class DevicesView(APIView):
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
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:
|
||||
logger.error(e)
|
||||
error_msg = 'Internal Server Error'
|
||||
@@ -4534,3 +4538,20 @@ class RepoGroupFolderPerm(APIView):
|
||||
logger.error(e)
|
||||
error_msg = 'Internal Server Error'
|
||||
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 {}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
from rest_framework import status
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from seaserv import seafile_api
|
||||
from seahub import settings
|
||||
from seahub.api2.base import APIView
|
||||
from seahub.api2.throttling import AnonRateThrottle, UserRateThrottle
|
||||
from seahub.api2.utils import json_response, api_error
|
||||
from seahub.api2.authentication import TokenAuthentication
|
||||
|
@@ -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 import settings
|
||||
from seahub.utils import HAS_OFFICE_CONVERTER, HAS_FILE_SEARCH
|
||||
|
@@ -68,7 +68,7 @@ def get_user_synced_repo_infos(username):
|
||||
|
||||
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:
|
||||
# For desktop client, we also remove the sync tokens
|
||||
msg = 'failed to delete_repo_tokens_by_peer_id'
|
||||
@@ -80,4 +80,7 @@ def do_unlink_device(username, platform, device_id):
|
||||
logger.exception(msg)
|
||||
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)
|
||||
|
@@ -11,6 +11,9 @@ define([
|
||||
'platform': this.get('platform'),
|
||||
'device_id': this.get('device_id')
|
||||
};
|
||||
if (options.wipe_device) {
|
||||
data['wipe_device'] = 'true';
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: Common.getUrl({name: 'devices'}),
|
||||
|
@@ -73,9 +73,13 @@ define([
|
||||
device_name = this.model.get('device_name');
|
||||
var title = gettext('Unlink 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({
|
||||
wipe_device: wipe_device,
|
||||
|
||||
success: function() {
|
||||
_this.remove();
|
||||
|
||||
@@ -92,7 +96,7 @@ define([
|
||||
});
|
||||
return false;
|
||||
};
|
||||
Common.showConfirm(title, content, yesCallback);
|
||||
Common.showConfirmWithExtraOption(title, content, extraOption, yesCallback);
|
||||
}
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user