1
0
mirror of https://github.com/haiwen/seafile-server.git synced 2025-09-01 23:46:53 +00:00

Fix the bug that can't sync subdir in virtual repos.

This commit is contained in:
ly1217
2019-02-24 21:32:46 -08:00
parent 0547e9b664
commit 17d74c48bb
5 changed files with 326 additions and 37 deletions

View File

@@ -111,12 +111,96 @@ check_repo_share_permission (SeafRepoManager *mgr,
return NULL;
}
// get dir perm from all dir perms in parent repo
// such as path /a/b, then check /a/b, /a in parent
static char *
get_dir_perm (GHashTable *perms, const char *path)
{
char *tmp = g_strdup (path);
char *slash;
char *perm = NULL;
while (g_strcmp0 (tmp, "") != 0) {
perm = g_hash_table_lookup (perms, tmp);
if (perm)
break;
slash = g_strrstr (tmp, "/");
*slash = '\0';
}
g_free (tmp);
return g_strdup (perm);
}
static char *
check_perm_on_parent_repo (const char *origin_repo_id,
const char *user,
const char *vpath)
{
GHashTable *user_perms = NULL;
GHashTable *group_perms = NULL;
SearpcClient *rpc_client;
GList *groups = NULL;
GList *iter;
char *perm = NULL;
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"ccnet-threaded-rpcserver");
if (!rpc_client) {
return NULL;
}
user_perms = seaf_share_manager_get_shared_dirs_to_user (seaf->share_mgr,
origin_repo_id,
user);
if (!user_perms) {
ccnet_rpc_client_free (rpc_client);
return NULL;
}
if (g_hash_table_size (user_perms) > 0) {
perm = get_dir_perm (user_perms, vpath);
if (perm) {
g_hash_table_destroy (user_perms);
ccnet_rpc_client_free (rpc_client);
return perm;
}
}
g_hash_table_destroy (user_perms);
groups = ccnet_get_groups_by_user (rpc_client, user, 1);
ccnet_rpc_client_free (rpc_client);
if (!groups) {
return NULL;
}
group_perms = seaf_share_manager_get_shared_dirs_to_group (seaf->share_mgr,
origin_repo_id,
groups);
for (iter = groups; iter; iter = iter->next)
g_object_unref ((GObject *)iter->data);
g_list_free (groups);
if (!group_perms) {
return NULL;
}
if (g_hash_table_size (group_perms) > 0) {
perm = get_dir_perm (group_perms, vpath);
}
g_hash_table_destroy (group_perms);
return perm;
}
static char *
check_virtual_repo_permission (SeafRepoManager *mgr,
const char *repo_id,
const char *origin_repo_id,
const char *user,
GError **error)
const char *vpath)
{
char *owner = NULL;
char *permission = NULL;
@@ -134,11 +218,14 @@ check_virtual_repo_permission (SeafRepoManager *mgr,
* from a shared repo by me or directly shared by others to me.
* The priority of shared sub-folder is higher than top-level repo.
*/
permission = check_repo_share_permission (mgr, repo_id, user);
if (permission)
permission = check_perm_on_parent_repo (origin_repo_id,
user, vpath);
if (permission) {
return permission;
}
permission = check_repo_share_permission (mgr, origin_repo_id, user);
return permission;
}
@@ -162,7 +249,7 @@ seaf_repo_manager_check_permission (SeafRepoManager *mgr,
if (vinfo) {
permission = check_virtual_repo_permission (mgr, repo_id,
vinfo->origin_repo_id,
user, error);
user, vinfo->path);
goto out;
}

View File

@@ -11,6 +11,7 @@
#include "seaf-db.h"
#include "log.h"
#include "seafile-error.h"
#include <ccnet/ccnet-object.h>
SeafShareManager *
seaf_share_manager_new (SeafileSession *seaf)
@@ -520,6 +521,111 @@ seaf_share_manager_list_repo_shared_group (SeafShareManager *mgr,
return shared_group;
}
static gboolean
get_shared_dirs_to_user (SeafDBRow *row, void *data)
{
GHashTable *dirs = data;
const char *path = seaf_db_row_get_column_text (row, 0);
const char *perm = seaf_db_row_get_column_text (row, 1);
g_hash_table_replace (dirs, g_strdup (path), g_strdup (perm));
return TRUE;
}
static gboolean
get_shared_dirs_to_group (SeafDBRow *row, void *data)
{
GHashTable *dirs = data;
const char *path = seaf_db_row_get_column_text (row, 0);
const char *perm = seaf_db_row_get_column_text (row, 1);
char *prev_perm = g_hash_table_lookup (dirs, path);
if (g_strcmp0 (perm, prev_perm) != 0 &&
(prev_perm == NULL || g_strcmp0 (prev_perm, "r") == 0)) {
g_hash_table_replace (dirs, g_strdup (path), g_strdup (perm));
}
return TRUE;
}
// Conver group id list to comma separated str
// [1, 2, 3] -> 1,2,3
static GString *
convert_group_list_to_str (GList *groups)
{
GList *iter = groups;
CcnetGroup *group;
int group_id;
GString *group_ids = g_string_new ("");
for (; iter; iter = iter->next) {
group = iter->data;
g_object_get (group, "id", &group_id, NULL);
g_string_append_printf (group_ids, "%d,", group_id);
}
group_ids = g_string_erase (group_ids, group_ids->len - 1, 1);
return group_ids;
}
GHashTable *
seaf_share_manager_get_shared_dirs_to_user (SeafShareManager *mgr,
const char *orig_repo_id,
const char *to_email)
{
GHashTable *dirs;
char *sql;
dirs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
sql = "SELECT v.path, s.permission FROM SharedRepo s, VirtualRepo v WHERE "
"s.repo_id = v.repo_id AND s.to_email = ? AND v.origin_repo = ?";
int ret = seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_shared_dirs_to_user,
dirs, 2, "string", to_email,
"string", orig_repo_id);
if (ret < 0) {
seaf_warning ("Failed to get all shared folder perms "
"in parent repo %.8s for user %s.\n", orig_repo_id, to_email);
g_hash_table_destroy (dirs);
return NULL;
}
return dirs;
}
GHashTable *
seaf_share_manager_get_shared_dirs_to_group (SeafShareManager *mgr,
const char *orig_repo_id,
GList *groups)
{
GHashTable *dirs;
GString *group_ids;
char *sql;
dirs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
group_ids = convert_group_list_to_str (groups);
sql = g_strdup_printf ("SELECT v.path, s.permission "
"FROM RepoGroup s, VirtualRepo v WHERE "
"s.repo_id = v.repo_id AND v.origin_repo = ? "
"AND s.group_id in (%s)", group_ids->str);
int ret = seaf_db_statement_foreach_row (mgr->seaf->db, sql, get_shared_dirs_to_group,
dirs, 1, "string", orig_repo_id);
g_free (sql);
g_string_free (group_ids, TRUE);
if (ret < 0) {
seaf_warning ("Failed to get all shared folder perm from parent repo %.8s "
"to all user groups.\n", orig_repo_id);
g_hash_table_destroy (dirs);
return NULL;
}
return dirs;
}
int
seaf_share_manager_remove_share (SeafShareManager *mgr, const char *repo_id,
const char *from_email, const char *to_email)

View File

@@ -58,6 +58,16 @@ seaf_share_manager_list_repo_shared_group (SeafShareManager *mgr,
const char *repo_id,
GError **error);
GHashTable *
seaf_share_manager_get_shared_dirs_to_user (SeafShareManager *mgr,
const char *orig_repo_id,
const char *to_email);
GHashTable *
seaf_share_manager_get_shared_dirs_to_group (SeafShareManager *mgr,
const char *orig_repo_id,
GList *groups);
int
seaf_share_manager_remove_share (SeafShareManager *mgr, const char *repo_id,
const char *from_email, const char *to_email);

View File

@@ -164,32 +164,20 @@ get_existing_virtual_repo (SeafRepoManager *mgr,
"string", origin_repo_id, "string", path);
}
char *
seaf_repo_manager_create_virtual_repo (SeafRepoManager *mgr,
const char *origin_repo_id,
const char *path,
const char *repo_name,
const char *repo_desc,
const char *owner,
const char *passwd,
GError **error)
static char *
create_virtual_repo_common (SeafRepoManager *mgr,
const char *origin_repo_id,
const char *path,
const char *repo_name,
const char *repo_desc,
const char *owner,
const char *passwd,
GError **error)
{
SeafRepo *origin_repo = NULL;
SeafCommit *origin_head = NULL;
char *repo_id = NULL;
char *dir_id = NULL;
char *orig_owner = NULL;
if (seaf_repo_manager_is_virtual_repo (mgr, origin_repo_id)) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Cannot create sub-library from a sub-library");
return NULL;
}
repo_id = get_existing_virtual_repo (mgr, origin_repo_id, path);
if (repo_id) {
return repo_id;
}
origin_repo = seaf_repo_manager_get_repo (mgr, origin_repo_id);
if (!origin_repo) {
@@ -261,26 +249,16 @@ seaf_repo_manager_create_virtual_repo (SeafRepoManager *mgr,
goto error;
}
orig_owner = seaf_repo_manager_get_repo_owner (mgr, origin_repo_id);
if (do_create_virtual_repo (mgr, origin_repo, repo_id, repo_name, repo_desc,
dir_id, orig_owner, passwd, error) < 0)
dir_id, owner, passwd, error) < 0)
goto error;
if (seaf_repo_manager_set_repo_owner (mgr, repo_id, orig_owner) < 0) {
seaf_warning ("Failed to set repo owner for %.10s.\n", repo_id);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Failed to set repo owner.");
goto error;
}
/* The size of virtual repo is non-zero at the beginning. */
update_repo_size (repo_id);
seaf_repo_unref (origin_repo);
seaf_commit_unref (origin_head);
g_free (dir_id);
g_free (orig_owner);
return repo_id;
error:
@@ -288,10 +266,101 @@ error:
seaf_commit_unref (origin_head);
g_free (repo_id);
g_free (dir_id);
g_free (orig_owner);
return NULL;
}
static char *
canonical_vrepo_path (const char *path)
{
char *ret = NULL;
if (path[0] != '/')
ret = g_strconcat ("/", path, NULL);
else
ret = g_strdup(path);
int len = strlen(ret);
int i = len - 1;
while (i >= 0 && ret[i] == '/')
ret[i--] = 0;
return ret;
}
char *
seaf_repo_manager_create_virtual_repo (SeafRepoManager *mgr,
const char *origin_repo_id,
const char *path,
const char *repo_name,
const char *repo_desc,
const char *owner,
const char *passwd,
GError **error)
{
char *repo_id = NULL;
char *orig_owner = NULL;
char *canon_path = NULL;
SeafVirtRepo *vrepo = NULL;
char *r_origin_repo_id = NULL;
char *r_path = NULL;
if (g_strcmp0 (path, "/") == 0) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Invalid path");
return NULL;
}
canon_path = canonical_vrepo_path (path);
vrepo = seaf_repo_manager_get_virtual_repo_info (mgr, origin_repo_id);
if (vrepo) {
// virtual repo
r_path = g_strconcat(vrepo->path, canon_path, NULL);
r_origin_repo_id = g_strdup (vrepo->origin_repo_id);
seaf_virtual_repo_info_free (vrepo);
repo_id = get_existing_virtual_repo (mgr, r_origin_repo_id, r_path);
if (repo_id) {
g_free (r_origin_repo_id);
g_free (r_path);
g_free (canon_path);
return repo_id;
}
} else {
r_path = g_strdup (canon_path);
r_origin_repo_id = g_strdup (origin_repo_id);
repo_id = get_existing_virtual_repo (mgr, r_origin_repo_id, r_path);
if (repo_id) {
g_free (r_origin_repo_id);
g_free (r_path);
g_free (canon_path);
return repo_id;
}
}
orig_owner = seaf_repo_manager_get_repo_owner (mgr, r_origin_repo_id);
repo_id = create_virtual_repo_common (mgr, r_origin_repo_id, r_path,
repo_name, repo_desc, orig_owner,
passwd, error);
if (!repo_id) {
goto out;
}
if (seaf_repo_manager_set_repo_owner (mgr, repo_id, orig_owner) < 0) {
seaf_warning ("Failed to set repo owner for %.10s.\n", repo_id);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Failed to set repo owner.");
g_free (repo_id);
repo_id = NULL;
}
out:
g_free (orig_owner);
g_free (r_origin_repo_id);
g_free (r_path);
g_free (canon_path);
return repo_id;
}
static gboolean
load_virtual_info (SeafDBRow *row, void *p_vinfo)
{

View File

@@ -197,3 +197,20 @@ def test_get_shared_users_by_repo(repo, group, permission):
api.remove_share(repo.id, USER, USER2)
api.group_unshare_repo(repo.id, group.id, USER)
@pytest.mark.parametrize('permission', ['r', 'rw'])
def test_subdir_permission_in_virtual_repo(repo, group, permission):
api.post_dir(repo.id, '/dir1', 'subdir1', USER)
api.post_dir(repo.id, '/dir2', 'subdir2', USER)
v_repo_id_1 = api.share_subdir_to_user(repo.id, '/dir1', USER, USER2, permission)
v_subdir_repo_id_1 = api.create_virtual_repo(v_repo_id_1, '/subdir1', 'subdir1', 'test_desc', USER, passwd='')
assert api.check_permission(v_subdir_repo_id_1, USER2) == permission
assert ccnet_api.group_add_member(group.id, USER, USER2) == 0
v_repo_id_2 = api.share_subdir_to_group(repo.id, '/dir2', USER, group.id, permission)
v_subdir_repo_id_2 = api.create_virtual_repo(v_repo_id_2, '/subdir2', 'subdir2', 'test_desc', USER, passwd='')
assert api.check_permission(v_subdir_repo_id_2, USER2) == permission
assert api.unshare_subdir_for_user(repo.id, '/dir1', USER, USER2) == 0
assert api.unshare_subdir_for_group(repo.id, '/dir2', USER, group.id) == 0