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