diff --git a/api/urls.py b/api/urls.py index 7613953ff5..b026215589 100644 --- a/api/urls.py +++ b/api/urls.py @@ -4,12 +4,14 @@ from views import * urlpatterns = patterns('', - url(r'^/$', list_repo), - url(r'^repo/list/$', list_repo), - url(r'^repo/(?P[^/]+)/$', get_repo_info), - url(r'^dir/(?P[^/]+)/root/$', get_repo_dir_path), - url(r'^dir/(?P[^/]+)/$', get_repo_dir_path), - url(r'^dir/(?P[^/]+)/(?P[^/]+)/$', get_repo_dir_id), - url(r'^file/(?P[^/]+)/(?P[^/]+)/$', get_repo_file_id), + url(r'^$', ReposView.as_view()), + url(r'^/$', ReposView.as_view()), + url(r'^repo/list/$', ReposView.as_view(), name='repos'), + url(r'^repo/(?P[^/]+)/$', RepoView.as_view(), name='repo'), + + url(r'^dir/(?P[^/]+)/root/$', RepoDirPathView.as_view()), + url(r'^dir/(?P[^/]+)/$', RepoDirPathView.as_view(), name='repo-dir-path'), + url(r'^dir/(?P[^/]+)/(?P[^/]+)/$', RepoDirIdView.as_view(), name='repo-dirr-id'), + url(r'^file/(?P[^/]+)/(?P[^/]+)/$', RepoFileView.as_view(), name='repo-file'), ) diff --git a/api/views.py b/api/views.py index 3fc27a5b93..b2c1e61d61 100644 --- a/api/views.py +++ b/api/views.py @@ -6,7 +6,7 @@ import stat import simplejson as json from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError -from auth.decorators import login_required +from auth.decorators import login_required, api_login_required from seaserv import ccnet_rpc, ccnet_threaded_rpc, get_groups, get_users, get_repos, \ get_repo, get_commits, get_branches, \ @@ -19,6 +19,15 @@ from seahub.utils import list_to_string, \ check_filename_with_rename, get_accessible_repos, EMPTY_SHA1 from seahub.views import access_to_repo, validate_owner +from pysearpc import SearpcError + +from djangorestframework.renderers import JSONRenderer +from djangorestframework.compat import View +from djangorestframework.mixins import ResponseMixin +from djangorestframework.response import Response +from django.core.urlresolvers import reverse + + json_content_type = 'application/json; charset=utf-8' @@ -26,7 +35,7 @@ json_content_type = 'application/json; charset=utf-8' def api_error(request, code='404', msg=None): err_resp = {'error_msg':msg} return HttpResponse(json.dumps(err_resp), status=code, - content_type=json_content_type) + content_type=json_content_type) def can_access_repo(request, repo_id): repo_ap = seafserv_threaded_rpc.repo_query_access_property(repo_id) @@ -36,6 +45,7 @@ def can_access_repo(request, repo_id): # check whether user can view repo return access_to_repo(request, repo_id, repo_ap) + def get_dir_entrys_by_path(reqquest, commit, path): dentrys = [] if path[-1] != '/': @@ -43,8 +53,8 @@ def get_dir_entrys_by_path(reqquest, commit, path): if not commit.root_id == EMPTY_SHA1: try: - dirs = seafserv_rpc.list_dir_by_path(commit.id, - path.encode('utf-8')) + dirs = seafserv_threaded_rpc.list_dir_by_path(commit.id, + path.encode('utf-8')) except SearpcError, e: return api_error(request, "404", e.msg) for dirent in dirs: @@ -67,7 +77,7 @@ def get_dir_entrys_by_path(reqquest, commit, path): def get_dir_entrys_by_id(reqquest, dir_id): dentrys = [] try: - dirs = seafserv_rpc.list_dir(dir_id) + dirs = seafserv_threaded_rpc.list_dir(dir_id) except SearpcError, e: return api_error(request, "404", e.msg) for dirent in dirs: @@ -87,79 +97,85 @@ def get_dir_entrys_by_id(reqquest, dir_id): return HttpResponse(json.dumps(dentrys), status=200, content_type=json_content_type) -@login_required -def list_repo(request): - email = request.user.username +class ReposView(ResponseMixin, View): + renderers = (JSONRenderer,) - owned_repos = seafserv_threaded_rpc.list_owned_repos(email) - calculate_repo_last_modify(owned_repos) - owned_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify)) + @api_login_required + def get(self, request): + email = request.user.username - n_repos = seafserv_threaded_rpc.list_share_repos(email, - 'to_email', -1, -1) - calculate_repo_last_modify(owned_repos) - owned_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify)) + owned_repos = seafserv_threaded_rpc.list_owned_repos(email) + calculate_repo_last_modify(owned_repos) + owned_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify)) - repos_json = [] - for r in owned_repos: - repo = { - "id":r.props.id, - "owner":"self", - "name":r.props.name, - "desc":r.props.desc, - "mtime":r.lastest_modify, - } - repos_json.append(repo) + n_repos = seafserv_threaded_rpc.list_share_repos(email, + 'to_email', -1, -1) + calculate_repo_last_modify(owned_repos) + owned_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify)) - for r in n_repos: - repo = { - "id":r.props.id, - "owner":r.props.shared_email, - "name":r.props.name, - "desc":r.props.desc, - "mtime":r.lastest_modify, - } - repos_json.append(repo) - return HttpResponse(json.dumps(repos_json), status=200, - content_type=json_content_type) + repos_json = [] + for r in owned_repos: + repo = { + "id":r.props.id, + "owner":"self", + "name":r.props.name, + "desc":r.props.desc, + "mtime":r.lastest_modify, + } + repos_json.append(repo) + for r in n_repos: + repo = { + "id":r.props.id, + "owner":r.props.shared_email, + "name":r.props.name, + "desc":r.props.desc, + "mtime":r.lastest_modify, + } + repos_json.append(repo) -@login_required -def get_repo_info(request, repo_id): - # check whether user can view repo - if not can_access_repo(request, repo_id): - return api_error(request, '403', "can not access repo") + response = Response(200, repos_json) + return self.render(response) - # check whether use is repo owner - if validate_owner(request, repo_id): - owner = "self" - else: - owner = "share" - - repo = get_repo(repo_id) - if not repo: - return api_error(request, '404', "repo not found") +class RepoView(ResponseMixin, View): + renderers = (JSONRenderer,) - try: - repo.latest_modify = get_commits(repo.id, 0, 1)[0].ctime - except: - repo.latest_modify = None - # query whether set password if repo is encrypted + @api_login_required + def get_repo_info(request, repo_id): + # check whether user can view repo + if not can_access_repo(request, repo_id): + return api_error(request, '403', "can not access repo") + + # check whether use is repo owner + if validate_owner(request, repo_id): + owner = "self" + else: + owner = "share" + + repo = get_repo(repo_id) + if not repo: + return api_error(request, '404', "repo not found") - password_set = False - if repo.props.encrypted: try: - ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username) - if ret == 1: - password_set = True - except SearpcError, e: - return api_error(request, '403', e.msg) + repo.latest_modify = get_commits(repo.id, 0, 1)[0].ctime + except: + repo.latest_modify = None - # query repo infomation - repo_size = seafserv_threaded_rpc.server_repo_size(repo_id) - current_commit = get_commits(repo_id, 0, 1)[0] + # query whether set password if repo is encrypted + password_set = False + if repo.props.encrypted: + try: + ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username) + if ret == 1: + password_set = True + except SearpcError, e: + return api_error(request, '403', e.msg) - repo_json = { + # query repo infomation + repo_size = seafserv_threaded_rpc.server_repo_size(repo_id) + current_commit = get_commits(repo_id, 0, 1)[0] + + repo_json = { "id":repo.props.id, "owner":owner, "name":repo.props.name, @@ -169,90 +185,104 @@ def get_repo_info(request, repo_id): "size":repo_size, "commit":current_commit.id, } - - return HttpResponse(json.dumps(repo_json), status=200, - content_type=json_content_type) + + response = Response(200, repo_json) + return self.render(response) -@login_required -def get_repo_dir_path(request, repo_id): - if not can_access_repo(request, repo_id): - return api_error(request, '403', "can not access repo") - current_commit = get_commits(repo_id, 0, 1)[0] +class RepoDirPathView(ResponseMixin, View): + renderers = (JSONRenderer,) - repo = get_repo(repo_id) - if not repo: - return api_error(request, '404', "repo not found") + @api_login_required + def get(self, request, repo_id): + if not can_access_repo(request, repo_id): + return api_error(request, '403', "can not access repo") + current_commit = get_commits(repo_id, 0, 1)[0] - password_set = False - if repo.props.encrypted: - try: - ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username) - if ret == 1: - password_set = True - except SearpcError, e: - return api_error(request, '403', e.msg) - if repo.props.encrypted and not password_set: - return api_error(request, '403', "password needed") + repo = get_repo(repo_id) + if not repo: + return api_error(request, '404', "repo not found") - path = request.GET.get('p', '/') - return get_dir_entrys_by_path(request, current_commit, path) + password_set = False + if repo.props.encrypted: + try: + ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username) + if ret == 1: + password_set = True + except SearpcError, e: + return api_error(request, '403', e.msg) + + if repo.props.encrypted and not password_set: + return api_error(request, '403', "password needed") + + path = request.GET.get('p', '/') + return get_dir_entrys_by_path(request, current_commit, path) -@login_required -def get_repo_dir_id(request, repo_id, dir_id): - if not can_access_repo(request, repo_id): - return api_error(request, '403', "can not access repo") - repo = get_repo(repo_id) - if not repo: - return api_error(request, '404', "repo not found") +class RepoDirIdView(ResponseMixin, View): + renderers = (JSONRenderer,) - password_set = False - if repo.props.encrypted: - try: - ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username) - if ret == 1: - password_set = True - except SearpcError, e: - return api_error(request, '403', e.msg) + @api_login_required + def get(self, request, repo_id, dir_id): + if not can_access_repo(request, repo_id): + return api_error(request, '403', "can not access repo") - if repo.props.encrypted and not password_set: - return api_error(request, '403', "password needed") + repo = get_repo(repo_id) + if not repo: + return api_error(request, '404', "repo not found") - return get_dir_entrys_by_id(request, dir_id) + password_set = False + if repo.props.encrypted: + try: + ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username) + if ret == 1: + password_set = True + except SearpcError, e: + return api_error(request, '403', e.msg) -@login_required -def get_repo_file_id(request, repo_id, file_id): - if not can_access_repo(request, repo_id): - return api_error(request, '403', "can not access repo") + if repo.props.encrypted and not password_set: + return api_error(request, '403', "password needed") - repo = get_repo(repo_id) - if not repo: - return api_error(request, '404', "repo not found") + return get_dir_entrys_by_id(request, dir_id) - password_set = False - if repo.props.encrypted: - try: - ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username) - if ret == 1: - password_set = True - except SearpcError, e: - return api_error(request, '403', e.msg) - if repo.props.encrypted and not password_set: - return api_error(request, '403', "password needed") - file_name = request.GET.get('file_name', file_id) - token = gen_token() - # put token into memory in seaf-server - seafserv_rpc.web_save_access_token(token, file_id) +class RepoFileView(ResponseMixin, View): + renderers = (JSONRenderer,) - http_server_root = get_httpserver_root() - op='download' - redirect_url = '%s/access?repo_id=%s&id=%s&filename=%s&op=%s&t=%s&u=%s' % (http_server_root, + @api_login_required + def get(self, request, repo_id, file_id): + if not can_access_repo(request, repo_id): + return api_error(request, '403', "can not access repo") + + repo = get_repo(repo_id) + if not repo: + return api_error(request, '404', "repo not found") + + password_set = False + if repo.props.encrypted: + try: + ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username) + if ret == 1: + password_set = True + except SearpcError, e: + return api_error(request, '403', e.msg) + + if repo.props.encrypted and not password_set: + return api_error(request, '403', "password needed") + file_name = request.GET.get('file_name', file_id) + token = gen_token() + # put token into memory in seaf-server + seafserv_rpc.web_save_access_token(token, file_id) + + http_server_root = get_httpserver_root() + op='download' + redirect_url = '%s/access?repo_id=%s&id=%s&filename=%s&op=%s&t=%s&u=%s' % (http_server_root, repo_id, file_id, - file_name, op, + file_name, op, token, request.user.username) - return HttpResponseRedirect(redirect_url) + return HttpResponseRedirect(redirect_url) + + diff --git a/thirdpart/auth/decorators.py b/thirdpart/auth/decorators.py index ce9b320227..373abe7b09 100644 --- a/thirdpart/auth/decorators.py +++ b/thirdpart/auth/decorators.py @@ -44,6 +44,44 @@ def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME): return actual_decorator +def api_user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): + """ + Decorator for views that checks that the user passes the given test, + redirecting to the log-in page if necessary. The test should be a callable + that takes the user object and returns True if the user passes. + """ + if not login_url: + from django.conf import settings + login_url = settings.LOGIN_URL + + def decorator(view_func): + def _wrapped_view(obj, request, *args, **kwargs): + if test_func(request.user): + return view_func(obj, request, *args, **kwargs) + path = urlquote(request.get_full_path()) + tup = login_url, redirect_field_name, path + json_content_type = 'application/json; charset=utf-8' + + return HttpResponse(json.dumps('%s?%s=%s' % tup), status=401, + content_type=json_content_type) + return wraps(view_func, assigned=available_attrs(view_func))(_wrapped_view) + return decorator + + +def api_login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME): + """ + Decorator for views that checks that the user is logged in, redirecting + to the log-in page if necessary. + """ + actual_decorator = api_user_passes_test( + lambda u: u.is_authenticated(), + redirect_field_name=redirect_field_name + ) + if function: + return actual_decorator(function) + return actual_decorator + + def permission_required(perm, login_url=None): """ Decorator for views that checks whether a user has a particular permission diff --git a/urls.py b/urls.py index 44faf9703d..ffd65e2646 100644 --- a/urls.py +++ b/urls.py @@ -43,7 +43,6 @@ urlpatterns = patterns('', (r'^share/', include('share.urls')), (r'^api/', include('api.urls')), - (r'^rest/', include('djangorestframework.urls')), url(r'^shareadmin/$', share_admin, name='share_admin'), (r'^shareadmin/removeshare/$', repo_remove_share), (r'^sharedlink/get/$', get_shared_link),