Fossil SCM

fossil-scm / src / json_login.c
Blame History Raw 274 lines
1
#ifdef FOSSIL_ENABLE_JSON
2
/*
3
** Copyright (c) 2011 D. Richard Hipp
4
**
5
** This program is free software; you can redistribute it and/or
6
** modify it under the terms of the Simplified BSD License (also
7
** known as the "2-Clause License" or "FreeBSD License".)
8
**
9
** This program is distributed in the hope that it will be useful,
10
** but without any warranty; without even the implied warranty of
11
** merchantability or fitness for a particular purpose.
12
**
13
** Author contact information:
14
** [email protected]
15
** http://www.hwaci.com/drh/
16
**
17
*/
18
19
#include "config.h"
20
#include "json_login.h"
21
22
#if INTERFACE
23
#include "json_detail.h"
24
#endif
25
26
27
/*
28
** Implementation of the /json/login page.
29
**
30
*/
31
cson_value * json_page_login(void){
32
char preciseErrors = /* if true, "complete" JSON error codes are used,
33
else they are "dumbed down" to a generic login
34
error code.
35
*/
36
#if 1
37
g.json.errorDetailParanoia ? 0 : 1
38
#else
39
0
40
#endif
41
;
42
/*
43
FIXME: we want to check the GET/POST args in this order:
44
45
- GET: name, n, password, p
46
- POST: name, password
47
48
but fossil's age-old behaviour of treating the last element of
49
PATH_INFO as the value for the name parameter breaks that.
50
51
Summary: If we check for P("name") first, then P("n"), then ONLY a
52
GET param of "name" will match ("n" is not recognized). If we
53
reverse the order of the checks then both forms work. The
54
"p"/"password" check is not affected by this.
55
*/
56
char const * name = cson_value_get_cstr(json_req_payload_get("name"));
57
char const * pw = NULL;
58
char const * anonSeed = NULL;
59
cson_value * payload = NULL;
60
int uid = 0;
61
/* reminder to self: Fossil internally (for the sake of /wiki)
62
interprets paths in the form /foo/bar/baz such that P("name") ==
63
"bar/baz". This collides with our name/password checking, and
64
thus we do some rather elaborate name=... checking.
65
*/
66
pw = cson_value_get_cstr(json_req_payload_get("password"));
67
if( !pw ){
68
pw = PD("p",NULL);
69
if( !pw ){
70
pw = PD("password",NULL);
71
}
72
}
73
if(!pw){
74
g.json.resultCode = preciseErrors
75
? FSL_JSON_E_LOGIN_FAILED_NOPW
76
: FSL_JSON_E_LOGIN_FAILED;
77
return NULL;
78
}
79
80
if( !name ){
81
name = PD("n",NULL);
82
if( !name ){
83
name = PD("name",NULL);
84
if( !name ){
85
g.json.resultCode = preciseErrors
86
? FSL_JSON_E_LOGIN_FAILED_NONAME
87
: FSL_JSON_E_LOGIN_FAILED;
88
return NULL;
89
}
90
}
91
}
92
93
if(0 == strcmp("anonymous",name)){
94
/* check captcha/seed values... */
95
enum { SeedBufLen = 100 /* in some JSON tests i once actually got an
96
80-digit number.
97
*/
98
};
99
static char seedBuffer[SeedBufLen];
100
cson_value const * jseed = json_getenv(FossilJsonKeys.anonymousSeed);
101
seedBuffer[0] = 0;
102
if( !jseed ){
103
jseed = json_req_payload_get(FossilJsonKeys.anonymousSeed);
104
if( !jseed ){
105
jseed = json_getenv("cs") /* name used by HTML interface */;
106
}
107
}
108
if(jseed){
109
if( cson_value_is_number(jseed) ){
110
sqlite3_snprintf((int)SeedBufLen, seedBuffer, "%"CSON_INT_T_PFMT,
111
cson_value_get_integer(jseed));
112
anonSeed = seedBuffer;
113
}else if( cson_value_is_string(jseed) ){
114
anonSeed = cson_string_cstr(cson_value_get_string(jseed));
115
}
116
}
117
if(!anonSeed){
118
g.json.resultCode = preciseErrors
119
? FSL_JSON_E_LOGIN_FAILED_NOSEED
120
: FSL_JSON_E_LOGIN_FAILED;
121
return NULL;
122
}
123
}
124
125
#if 0
126
{
127
/* only for debugging the PD()-incorrect-result problem */
128
cson_object * o = NULL;
129
uid = login_search_uid( &name, pw );
130
payload = cson_value_new_object();
131
o = cson_value_get_object(payload);
132
cson_object_set( o, "n", cson_value_new_string(name,strlen(name)));
133
cson_object_set( o, "p", cson_value_new_string(pw,strlen(pw)));
134
return payload;
135
}
136
#endif
137
uid = anonSeed
138
? login_is_valid_anonymous(name, pw, anonSeed)
139
: login_search_uid(&name, pw)
140
;
141
if( !uid ){
142
g.json.resultCode = preciseErrors
143
? FSL_JSON_E_LOGIN_FAILED_NOTFOUND
144
: FSL_JSON_E_LOGIN_FAILED;
145
return NULL;
146
}else{
147
char * cookie = NULL;
148
cson_object * po;
149
char * cap = NULL;
150
if(anonSeed){
151
login_set_anon_cookie(&cookie, 0);
152
}else{
153
login_set_user_cookie(name, uid, &cookie, 0);
154
}
155
payload = cson_value_new_object();
156
po = cson_value_get_object(payload);
157
cson_object_set(po, "authToken", json_new_string(cookie));
158
free(cookie);
159
cson_object_set(po, "name", json_new_string(name));
160
cap = db_text(NULL, "SELECT cap FROM user WHERE login=%Q", name);
161
cson_object_set(po, "capabilities",
162
cap ? json_new_string(cap) : cson_value_null() );
163
free(cap);
164
cson_object_set(po, "loginCookieName",
165
json_new_string( login_cookie_name() ) );
166
/* TODO: add loginExpiryTime to the payload. To do this properly
167
we "should" add an ([unsigned] int *) to
168
login_set_user_cookie() and login_set_anon_cookie(), to which
169
the expiry time is assigned. (Remember that JSON doesn't do
170
unsigned int.)
171
172
For non-anonymous users we could also simply query the
173
user.cexpire db field after calling login_set_user_cookie(),
174
but for anonymous we need to get the time when the cookie is
175
set because anon does not get a db entry like normal users
176
do. Anonymous cookies currently have a hard-coded lifetime in
177
login_set_anon_cookie() (currently 6 hours), which we "should
178
arguably" change to use the time configured for non-anonymous
179
users (see login_set_user_cookie() for details).
180
*/
181
return payload;
182
}
183
}
184
185
/*
186
** Impl of /json/logout.
187
**
188
*/
189
cson_value * json_page_logout(void){
190
cson_value const *token = g.json.authToken;
191
/* Remember that json_bootstrap_late() replaces the login cookie
192
with the JSON auth token if the request contains it. If the
193
request is missing the auth token then this will fetch fossil's
194
original cookie. Either way, it's what we want :).
195
196
We require the auth token to avoid someone maliciously
197
trying to log someone else out (not 100% sure if that
198
would be possible, given fossil's hardened cookie, but
199
I'll assume it would be for the time being).
200
*/
201
;
202
if(!token){
203
g.json.resultCode = FSL_JSON_E_MISSING_AUTH;
204
}else{
205
login_clear_login_data();
206
g.json.authToken = NULL /* memory is owned elsewhere.*/;
207
json_setenv(FossilJsonKeys.authToken, NULL);
208
}
209
return json_page_whoami();
210
}
211
212
/*
213
** Implementation of the /json/anonymousPassword page.
214
*/
215
cson_value * json_page_anon_password(void){
216
cson_value * v = cson_value_new_object();
217
cson_object * o = cson_value_get_object(v);
218
unsigned const int seed = captcha_seed();
219
char const * zCaptcha = captcha_decode(seed, 0);
220
cson_object_set(o, "seed",
221
cson_value_new_integer( (cson_int_t)seed )
222
);
223
cson_object_set(o, "password",
224
cson_value_new_string( zCaptcha, strlen(zCaptcha) )
225
);
226
return v;
227
}
228
229
230
231
/*
232
** Implements the /json/whoami page/command.
233
*/
234
cson_value * json_page_whoami(void){
235
cson_value * payload = NULL;
236
cson_object * obj = NULL;
237
Stmt q;
238
if(!g.json.authToken && g.userUid==0){
239
/* assume we just logged out. */
240
db_prepare(&q, "SELECT login, cap FROM user WHERE login='nobody'");
241
}
242
else{
243
db_prepare(&q, "SELECT login, cap FROM user WHERE uid=%d",
244
g.userUid);
245
}
246
if( db_step(&q)==SQLITE_ROW ){
247
248
/* reminder: we don't use g.zLogin because it's 0 for the guest
249
user and the HTML UI appears to currently allow the name to be
250
changed (but doing so would break other code). */
251
char const * str;
252
payload = cson_value_new_object();
253
obj = cson_value_get_object(payload);
254
str = (char const *)sqlite3_column_text(q.pStmt,0);
255
if( str ){
256
cson_object_set( obj, "name",
257
cson_value_new_string(str,strlen(str)) );
258
}
259
str = (char const *)sqlite3_column_text(q.pStmt,1);
260
if( str ){
261
cson_object_set( obj, "capabilities",
262
cson_value_new_string(str,strlen(str)) );
263
}
264
if( g.json.authToken ){
265
cson_object_set( obj, "authToken", g.json.authToken );
266
}
267
}else{
268
g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
269
}
270
db_finalize(&q);
271
return payload;
272
}
273
#endif /* FOSSIL_ENABLE_JSON */
274

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button