1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3 #include "common.h"
4
5 #include "utils.h"
6 #include "log.h"
7
8 #include "seafile-session.h"
9 #include "repo-mgr.h"
10
11 #include "seafile-error.h"
12 #include "seaf-utils.h"
13 /*
14 * Permission priority: owner --> personal share --> group share --> public.
15 * Permission with higher priority overwrites those with lower priority.
16 */
17
18 static gboolean
check_repo_share_perm_cb(SeafDBRow * row,void * data)19 check_repo_share_perm_cb (SeafDBRow *row, void *data)
20 {
21 char **orig_perm = data;
22 char *perm = g_strdup (seaf_db_row_get_column_text (row, 0));
23
24 if (g_strcmp0(perm, "rw") == 0) {
25 g_free (*orig_perm);
26 *orig_perm = perm;
27 return FALSE;
28 } else if (g_strcmp0(perm, "r") == 0 && !(*orig_perm)) {
29 *orig_perm = perm;
30 return TRUE;
31 }
32
33 g_free (perm);
34 return TRUE;
35 }
36
37 static char *
check_group_permission_by_user(SeafRepoManager * mgr,const char * repo_id,const char * user_name)38 check_group_permission_by_user (SeafRepoManager *mgr,
39 const char *repo_id,
40 const char *user_name)
41 {
42 char *permission = NULL;
43 GList *groups = NULL, *p1;
44 CcnetGroup *group;
45 int group_id;
46 GString *sql;
47
48 /* Get the groups this user belongs to. */
49 groups = ccnet_group_manager_get_groups_by_user (seaf->group_mgr, user_name,
50 1, NULL);
51 if (!groups) {
52 goto out;
53 }
54
55 sql = g_string_new ("");
56 g_string_printf (sql, "SELECT permission FROM RepoGroup WHERE repo_id = ? AND group_id IN (");
57 for (p1 = groups; p1 != NULL; p1 = p1->next) {
58 group = p1->data;
59 g_object_get (group, "id", &group_id, NULL);
60
61 g_string_append_printf (sql, "%d", group_id);
62 if (p1->next)
63 g_string_append_printf (sql, ",");
64 }
65 g_string_append_printf (sql, ")");
66
67 if (seaf_db_statement_foreach_row (mgr->seaf->db, sql->str,
68 check_repo_share_perm_cb, &permission,
69 1, "string", repo_id) < 0) {
70 seaf_warning ("DB error when get repo share permission for repo %s.\n", repo_id);
71 }
72
73 g_string_free (sql, TRUE);
74
75 out:
76 for (p1 = groups; p1 != NULL; p1 = p1->next)
77 g_object_unref ((GObject *)p1->data);
78 g_list_free (groups);
79 return permission;
80 }
81
82 static char *
check_repo_share_permission(SeafRepoManager * mgr,const char * repo_id,const char * user_name)83 check_repo_share_permission (SeafRepoManager *mgr,
84 const char *repo_id,
85 const char *user_name)
86 {
87 char *permission;
88
89 permission = seaf_share_manager_check_permission (seaf->share_mgr,
90 repo_id,
91 user_name);
92 if (permission != NULL)
93 return permission;
94
95 permission = check_group_permission_by_user (mgr, repo_id, user_name);
96 if (permission != NULL)
97 return permission;
98
99 if (!mgr->seaf->cloud_mode)
100 return seaf_repo_manager_get_inner_pub_repo_perm (mgr, repo_id);
101
102 return NULL;
103 }
104
105 // get dir perm from all dir perms in parent repo
106 // such as path /a/b, then check /a/b, /a in parent
107 static char *
get_dir_perm(GHashTable * perms,const char * path)108 get_dir_perm (GHashTable *perms, const char *path)
109 {
110 char *tmp = g_strdup (path);
111 char *slash;
112 char *perm = NULL;
113
114 while (g_strcmp0 (tmp, "") != 0) {
115 perm = g_hash_table_lookup (perms, tmp);
116 if (perm)
117 break;
118 slash = g_strrstr (tmp, "/");
119 *slash = '\0';
120 }
121
122 g_free (tmp);
123
124 return g_strdup (perm);
125 }
126
127 static char *
check_perm_on_parent_repo(const char * origin_repo_id,const char * user,const char * vpath)128 check_perm_on_parent_repo (const char *origin_repo_id,
129 const char *user,
130 const char *vpath)
131 {
132 GHashTable *user_perms = NULL;
133 GHashTable *group_perms = NULL;
134 GList *groups = NULL;
135 GList *iter;
136 char *perm = NULL;
137
138 user_perms = seaf_share_manager_get_shared_dirs_to_user (seaf->share_mgr,
139 origin_repo_id,
140 user);
141
142 if (!user_perms) {
143 return NULL;
144 }
145
146 if (g_hash_table_size (user_perms) > 0) {
147 perm = get_dir_perm (user_perms, vpath);
148 if (perm) {
149 g_hash_table_destroy (user_perms);
150 return perm;
151 }
152 }
153 g_hash_table_destroy (user_perms);
154
155 groups = ccnet_group_manager_get_groups_by_user (seaf->group_mgr, user,
156 1, NULL);
157 if (!groups) {
158 return NULL;
159 }
160
161 group_perms = seaf_share_manager_get_shared_dirs_to_group (seaf->share_mgr,
162 origin_repo_id,
163 groups);
164
165 for (iter = groups; iter; iter = iter->next)
166 g_object_unref ((GObject *)iter->data);
167 g_list_free (groups);
168
169 if (!group_perms) {
170 return NULL;
171 }
172 if (g_hash_table_size (group_perms) > 0) {
173 perm = get_dir_perm (group_perms, vpath);
174 }
175 g_hash_table_destroy (group_perms);
176
177 return perm;
178 }
179
180 static char *
check_virtual_repo_permission(SeafRepoManager * mgr,const char * repo_id,const char * origin_repo_id,const char * user,const char * vpath)181 check_virtual_repo_permission (SeafRepoManager *mgr,
182 const char *repo_id,
183 const char *origin_repo_id,
184 const char *user,
185 const char *vpath)
186 {
187 char *owner = NULL;
188 char *permission = NULL;
189
190 /* If I'm the owner of origin repo, I have full access to sub-repos. */
191 owner = seaf_repo_manager_get_repo_owner (mgr, origin_repo_id);
192 if (g_strcmp0 (user, owner) == 0) {
193 g_free (owner);
194 permission = g_strdup("rw");
195 return permission;
196 }
197 g_free (owner);
198
199 /* If I'm not the owner of origin repo, this sub-repo can be created
200 * from a shared repo by me or directly shared by others to me.
201 * The priority of shared sub-folder is higher than top-level repo.
202 */
203 permission = check_perm_on_parent_repo (origin_repo_id,
204 user, vpath);
205 if (permission) {
206 return permission;
207 }
208
209 permission = check_repo_share_permission (mgr, origin_repo_id, user);
210
211 return permission;
212 }
213
214 /*
215 * Comprehensive repo access permission checker.
216 *
217 * Returns read/write permission.
218 */
219 char *
seaf_repo_manager_check_permission(SeafRepoManager * mgr,const char * repo_id,const char * user,GError ** error)220 seaf_repo_manager_check_permission (SeafRepoManager *mgr,
221 const char *repo_id,
222 const char *user,
223 GError **error)
224 {
225 SeafVirtRepo *vinfo;
226 char *owner = NULL;
227 char *permission = NULL;
228
229 /* This is a virtual repo.*/
230 vinfo = seaf_repo_manager_get_virtual_repo_info (mgr, repo_id);
231 if (vinfo) {
232 permission = check_virtual_repo_permission (mgr, repo_id,
233 vinfo->origin_repo_id,
234 user, vinfo->path);
235 goto out;
236 }
237
238 owner = seaf_repo_manager_get_repo_owner (mgr, repo_id);
239 if (owner != NULL) {
240 if (strcmp (owner, user) == 0)
241 permission = g_strdup("rw");
242 else
243 permission = check_repo_share_permission (mgr, repo_id, user);
244 }
245
246 out:
247 seaf_virtual_repo_info_free (vinfo);
248 g_free (owner);
249 return permission;
250 }
251
252 /*
253 * Directories are always before files. Otherwise compare the names.
254 */
255 static gint
comp_dirent_func(gconstpointer a,gconstpointer b)256 comp_dirent_func (gconstpointer a, gconstpointer b)
257 {
258 const SeafDirent *dent_a = a, *dent_b = b;
259
260 if (S_ISDIR(dent_a->mode) && S_ISREG(dent_b->mode))
261 return -1;
262
263 if (S_ISREG(dent_a->mode) && S_ISDIR(dent_b->mode))
264 return 1;
265
266 return strcasecmp (dent_a->name, dent_b->name);
267 }
268
269 GList *
seaf_repo_manager_list_dir_with_perm(SeafRepoManager * mgr,const char * repo_id,const char * dir_path,const char * dir_id,const char * user,int offset,int limit,GError ** error)270 seaf_repo_manager_list_dir_with_perm (SeafRepoManager *mgr,
271 const char *repo_id,
272 const char *dir_path,
273 const char *dir_id,
274 const char *user,
275 int offset,
276 int limit,
277 GError **error)
278 {
279 SeafRepo *repo;
280 char *perm = NULL;
281 SeafDir *dir;
282 SeafDirent *dent;
283 SeafileDirent *d;
284 GList *res = NULL;
285 GList *p;
286
287 perm = seaf_repo_manager_check_permission (mgr, repo_id, user, error);
288 if (!perm) {
289 if (*error == NULL)
290 g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Access denied");
291 return NULL;
292 }
293
294 repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
295 if (!repo) {
296 g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Bad repo id");
297 g_free (perm);
298 return NULL;
299 }
300
301 dir = seaf_fs_manager_get_seafdir (seaf->fs_mgr,
302 repo->store_id, repo->version, dir_id);
303 if (!dir) {
304 g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_DIR_ID, "Bad dir id");
305 seaf_repo_unref (repo);
306 g_free (perm);
307 return NULL;
308 }
309
310 dir->entries = g_list_sort (dir->entries, comp_dirent_func);
311
312 if (offset < 0) {
313 offset = 0;
314 }
315
316 int index = 0;
317 gboolean is_shared;
318 char *cur_path;
319 GHashTable *shared_sub_dirs = NULL;
320
321 if (!repo->virtual_info) {
322 char *repo_owner = seaf_repo_manager_get_repo_owner (seaf->repo_mgr, repo_id);
323 if (repo_owner && strcmp (user, repo_owner) == 0) {
324 shared_sub_dirs = seaf_share_manager_get_shared_sub_dirs (seaf->share_mgr,
325 repo->store_id,
326 dir_path);
327 }
328 g_free (repo_owner);
329 }
330
331 for (p = dir->entries; p != NULL; p = p->next, index++) {
332 if (index < offset) {
333 continue;
334 }
335
336 if (limit > 0) {
337 if (index >= offset + limit)
338 break;
339 }
340
341 dent = p->data;
342
343 if (!is_object_id_valid (dent->id))
344 continue;
345
346 d = g_object_new (SEAFILE_TYPE_DIRENT,
347 "obj_id", dent->id,
348 "obj_name", dent->name,
349 "mode", dent->mode,
350 "version", dent->version,
351 "mtime", dent->mtime,
352 "size", dent->size,
353 "permission", perm,
354 "modifier", dent->modifier,
355 NULL);
356
357 if (shared_sub_dirs && S_ISDIR(dent->mode)) {
358 if (strcmp (dir_path, "/") == 0) {
359 cur_path = g_strconcat (dir_path, dent->name, NULL);
360 } else {
361 cur_path = g_strconcat (dir_path, "/", dent->name, NULL);
362 }
363 is_shared = g_hash_table_lookup (shared_sub_dirs, cur_path) ? TRUE : FALSE;
364 g_free (cur_path);
365 g_object_set (d, "is_shared", is_shared, NULL);
366 }
367 res = g_list_prepend (res, d);
368 }
369
370 if (shared_sub_dirs)
371 g_hash_table_destroy (shared_sub_dirs);
372 seaf_dir_free (dir);
373 seaf_repo_unref (repo);
374 g_free (perm);
375 if (res)
376 res = g_list_reverse (res);
377
378 return res;
379 }
380