1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 #include "common.h"
4 
5 #include <timer.h>
6 
7 #include <pthread.h>
8 
9 #include "seafile-session.h"
10 #include "web-accesstoken-mgr.h"
11 #include "seafile-error.h"
12 
13 #include "utils.h"
14 
15 #include "log.h"
16 
17 #define CLEANING_INTERVAL_MSEC 1000*300	/* 5 minutes */
18 #define TOKEN_EXPIRE_TIME 3600	        /* 1 hour */
19 #define TOKEN_LEN 36
20 
21 struct WebATPriv {
22     GHashTable		*access_token_hash; /* token -> access info */
23     pthread_mutex_t lock;
24 
25     gboolean cluster_mode;
26     struct ObjCache *cache;
27 };
28 typedef struct WebATPriv WebATPriv;
29 
30 /* #define DEBUG 1 */
31 
32 typedef struct {
33     char *repo_id;
34     char *obj_id;
35     char *op;
36     char *username;
37     long expire_time;
38     gboolean use_onetime;
39 } AccessInfo;
40 
41 static void
free_access_info(AccessInfo * info)42 free_access_info (AccessInfo *info)
43 {
44     if (!info)
45         return;
46 
47     g_free (info->repo_id);
48     g_free (info->obj_id);
49     g_free (info->op);
50     g_free (info->username);
51     g_free (info);
52 }
53 
54 SeafWebAccessTokenManager*
seaf_web_at_manager_new(SeafileSession * session)55 seaf_web_at_manager_new (SeafileSession *session)
56 {
57     SeafWebAccessTokenManager *mgr = g_new0 (SeafWebAccessTokenManager, 1);
58 
59     mgr->seaf = session;
60 
61     mgr->priv = g_new0(WebATPriv, 1);
62     mgr->priv->access_token_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
63                                                     g_free,
64                                                     (GDestroyNotify)free_access_info);
65     pthread_mutex_init (&mgr->priv->lock, NULL);
66 
67     return mgr;
68 }
69 
70 static gboolean
remove_expire_info(gpointer key,gpointer value,gpointer user_data)71 remove_expire_info (gpointer key, gpointer value, gpointer user_data)
72 {
73     AccessInfo *info = (AccessInfo *)value;
74     long now = *((long*)user_data);
75 
76     if (info && now >= info->expire_time) {
77         return TRUE;
78     }
79 
80     return FALSE;
81 }
82 
83 static int
clean_pulse(void * vmanager)84 clean_pulse (void *vmanager)
85 {
86     SeafWebAccessTokenManager *manager = vmanager;
87     long now = (long)time(NULL);
88 
89     pthread_mutex_lock (&manager->priv->lock);
90 
91     g_hash_table_foreach_remove (manager->priv->access_token_hash,
92                                  remove_expire_info, &now);
93 
94     pthread_mutex_unlock (&manager->priv->lock);
95 
96     return TRUE;
97 }
98 
99 int
seaf_web_at_manager_start(SeafWebAccessTokenManager * mgr)100 seaf_web_at_manager_start (SeafWebAccessTokenManager *mgr)
101 {
102     ccnet_timer_new (clean_pulse, mgr, CLEANING_INTERVAL_MSEC);
103 
104     return 0;
105 }
106 
107 static char *
gen_new_token(GHashTable * token_hash)108 gen_new_token (GHashTable *token_hash)
109 {
110     char uuid[37];
111     char *token;
112 
113     while (1) {
114         gen_uuid_inplace (uuid);
115         token = g_strndup(uuid, TOKEN_LEN);
116 
117         /* Make sure the new token doesn't conflict with an existing one. */
118         if (g_hash_table_lookup (token_hash, token) != NULL)
119             g_free (token);
120         else
121             return token;
122     }
123 }
124 
125 char *
seaf_web_at_manager_get_access_token(SeafWebAccessTokenManager * mgr,const char * repo_id,const char * obj_id,const char * op,const char * username,int use_onetime,GError ** error)126 seaf_web_at_manager_get_access_token (SeafWebAccessTokenManager *mgr,
127                                       const char *repo_id,
128                                       const char *obj_id,
129                                       const char *op,
130                                       const char *username,
131                                       int use_onetime,
132                                       GError **error)
133 {
134     AccessInfo *info;
135     long now = (long)time(NULL);
136     long expire;
137     char *t;
138     SeafileWebAccess *webaccess;
139 
140     if (strcmp(op, "view") != 0 &&
141         strcmp(op, "download") != 0 &&
142         strcmp(op, "downloadblks") != 0 &&
143         strcmp(op, "download-dir") != 0 &&
144         strcmp(op, "download-multi") != 0 &&
145         strcmp(op, "download-link") != 0 &&
146         strcmp(op, "download-dir-link") != 0 &&
147         strcmp(op, "download-multi-link") != 0 &&
148         strcmp(op, "upload") != 0 &&
149         strcmp(op, "update") != 0 &&
150         strcmp(op, "upload-link") != 0 &&
151         strcmp(op, "upload-blks-api") != 0 &&
152         strcmp(op, "upload-blks-aj") != 0 &&
153         strcmp(op, "update-blks-api") != 0 &&
154         strcmp(op, "update-blks-aj") != 0) {
155         g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
156                      "Invalid operation type.");
157         return NULL;
158     }
159 
160     pthread_mutex_lock (&mgr->priv->lock);
161 
162     t = gen_new_token (mgr->priv->access_token_hash);
163     expire = now + seaf->http_server->web_token_expire_time;
164 
165     info = g_new0 (AccessInfo, 1);
166     info->repo_id = g_strdup (repo_id);
167     info->obj_id = g_strdup (obj_id);
168     info->op = g_strdup (op);
169     info->username = g_strdup (username);
170     info->expire_time = expire;
171     if (use_onetime) {
172         info->use_onetime = TRUE;
173     }
174 
175     g_hash_table_insert (mgr->priv->access_token_hash, g_strdup(t), info);
176 
177     pthread_mutex_unlock (&mgr->priv->lock);
178 
179     if (strcmp(op, "download-dir") == 0 ||
180         strcmp(op, "download-multi") == 0 ||
181         strcmp(op, "download-dir-link") == 0 ||
182         strcmp(op, "download-multi-link") == 0) {
183 
184         webaccess = g_object_new (SEAFILE_TYPE_WEB_ACCESS,
185                                   "repo_id", info->repo_id,
186                                   "obj_id", info->obj_id,
187                                   "op", info->op,
188                                   "username", info->username,
189                                   NULL);
190 
191         if (zip_download_mgr_start_zip_task (seaf->zip_download_mgr,
192                                              t, webaccess, error) < 0) {
193             pthread_mutex_lock (&mgr->priv->lock);
194             g_hash_table_remove (mgr->priv->access_token_hash, t);
195             pthread_mutex_unlock (&mgr->priv->lock);
196 
197             g_object_unref (webaccess);
198             g_free (t);
199             return NULL;
200         }
201         g_object_unref (webaccess);
202     }
203 
204     return t;
205 }
206 
207 SeafileWebAccess *
seaf_web_at_manager_query_access_token(SeafWebAccessTokenManager * mgr,const char * token)208 seaf_web_at_manager_query_access_token (SeafWebAccessTokenManager *mgr,
209                                         const char *token)
210 {
211     SeafileWebAccess *webaccess;
212     AccessInfo *info;
213 
214     pthread_mutex_lock (&mgr->priv->lock);
215     info = g_hash_table_lookup (mgr->priv->access_token_hash, token);
216     pthread_mutex_unlock (&mgr->priv->lock);
217 
218     if (info != NULL) {
219         long expire_time = info->expire_time;
220         long now = (long)time(NULL);
221 
222         if (now - expire_time >= 0) {
223             return NULL;
224         } else {
225             webaccess = g_object_new (SEAFILE_TYPE_WEB_ACCESS,
226                                       "repo_id", info->repo_id,
227                                       "obj_id", info->obj_id,
228                                       "op", info->op,
229                                       "username", info->username,
230                                       NULL);
231 
232             if (info->use_onetime) {
233                 pthread_mutex_lock (&mgr->priv->lock);
234                 g_hash_table_remove (mgr->priv->access_token_hash, token);
235                 pthread_mutex_unlock (&mgr->priv->lock);
236             }
237 
238             return webaccess;
239         }
240     }
241 
242     return NULL;
243 }
244