Fossil SCM

Implemented anonymous user login over JSON. Requires 2 requests (captcha-fetch and then login).

stephan 2011-09-18 08:11 UTC json
Commit cebf9919f87c6678313cada3bdcc29f689a0e2ce
3 files changed +96 -19 +4 -3 +69 -44
+96 -19
--- src/json.c
+++ src/json.c
@@ -64,10 +64,11 @@
6464
C(ASSERT,"Assertion failed");
6565
C(ALLOC,"Resource allocation failed");
6666
C(NYI,"Not yet implemented.");
6767
C(AUTH,"Authentication error");
6868
C(LOGIN_FAILED,"Login failed");
69
+ C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed.");
6970
C(LOGIN_FAILED_NONAME,"Login failed - name not supplied");
7071
C(LOGIN_FAILED_NOPW,"Login failed - password not supplied");
7172
C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found");
7273
C(MISSING_AUTH,"Authentication info missing from request");
7374
C(DENIED,"Access denied");
@@ -652,11 +653,11 @@
652653
** If resultCode is non-zero and payload is not NULL then this
653654
** function calls cson_value_free(payload) and does not insert the
654655
** payload into the response.
655656
**
656657
*/
657
-cson_value * json_response_skeleton( int resultCode,
658
+cson_value * json_create_response( int resultCode,
658659
cson_value * payload,
659660
char const * pMsg ){
660661
cson_value * v = NULL;
661662
cson_value * tmp = NULL;
662663
cson_object * o = NULL;
@@ -713,19 +714,33 @@
713714
tmp = cson_value_new_string(pMsg,strlen(pMsg));
714715
SET("resultText");
715716
}
716717
tmp = json_getenv("requestId");
717718
if( tmp ) cson_object_set( o, "requestId", tmp );
719
+
720
+ if(0){
721
+ if(g.json.cmd.v){/* this is only intended for my own testing...*/
722
+ tmp = g.json.cmd.v;
723
+ SET("$commandPath");
724
+ }
725
+ if(g.json.param.v){/* this is only intended for my own testing...*/
726
+ tmp = g.json.param.v;
727
+ SET("$params");
728
+ }
729
+ }
730
+
731
+ /* Only add the payload to SUCCESS responses. Else delete it. */
718732
if( NULL != payload ){
719733
if( resultCode ){
720734
cson_value_free(payload);
721735
payload = NULL;
722736
}else{
723737
tmp = payload;
724738
SET("payload");
725739
}
726740
}
741
+
727742
#undef SET
728743
729744
if(0){/*Only for debuggering, add some info to the response.*/
730745
tmp = cson_value_new_integer( g.json.cmd.offset );
731746
cson_object_set( o, "cmd.offset", tmp );
@@ -765,11 +780,11 @@
765780
cson_value * resp = NULL;
766781
rc = json_dumbdown_rc(rc);
767782
if( rc && !msg ){
768783
msg = json_err_str(rc);
769784
}
770
- resp = json_response_skeleton(rc, NULL, msg);
785
+ resp = json_create_response(rc, NULL, msg);
771786
if( g.isCGI ){
772787
Blob buf = empty_blob;
773788
cgi_reset_content();
774789
cson_output_Blob( resp, &buf, &g.json.outOpt );
775790
cgi_set_content(&buf);
@@ -892,11 +907,11 @@
892907
cson_value * json_page_login(void){
893908
static char preciseErrors =
894909
#if 0
895910
g.json.errorDetailParanoia ? 0 : 1
896911
#else
897
- 0
912
+ 1
898913
#endif
899914
;
900915
/*
901916
FIXME: we want to check the GET/POST args in this order:
902917
@@ -912,10 +927,13 @@
912927
checks then both forms work. Strangely enough, the
913928
"p"/"password" check is not affected by this.
914929
*/
915930
char const * name = cson_value_get_cstr(json_payload_property("name"));
916931
char const * pw = NULL;
932
+ char const * anonSeed = NULL;
933
+ cson_value * payload = NULL;
934
+ int uid = 0;
917935
if( !name ){
918936
name = PD("n",NULL);
919937
if( !name ){
920938
name = PD("name",NULL);
921939
if( !name ){
@@ -924,11 +942,11 @@
924942
: FSL_JSON_E_LOGIN_FAILED;
925943
return NULL;
926944
}
927945
}
928946
}
929
-
947
+
930948
pw = cson_value_get_cstr(json_payload_property("password"));
931949
if( !pw ){
932950
pw = PD("p",NULL);
933951
if( !pw ){
934952
pw = PD("password",NULL);
@@ -937,37 +955,78 @@
937955
if(!pw){
938956
g.json.resultCode = preciseErrors
939957
? FSL_JSON_E_LOGIN_FAILED_NOPW
940958
: FSL_JSON_E_LOGIN_FAILED;
941959
return NULL;
942
- }else{
943
- cson_value * payload = NULL;
944
- int uid = 0;
960
+ }
961
+
962
+ if(0 == strcmp("anonymous",name)){
963
+ /* check captcha/seed values... */
964
+ enum { SeedBufLen = 100 /* in some JSON tests i once actually got an
965
+ 80-digit number.
966
+ */
967
+ };
968
+ static char seedBuffer[SeedBufLen];
969
+ seedBuffer[0] = 0;
970
+ cson_value const * jseed = json_getenv("anonymousSeed");
971
+ if( !jseed ){
972
+ jseed = json_payload_property("anonymousSeed");
973
+ if( !jseed ){
974
+ jseed = json_getenv("cs") /* name used by HTML interface */;
975
+ }
976
+ }
977
+ if(jseed){
978
+ if( cson_value_is_number(jseed) ){
979
+ sprintf(seedBuffer, "%"CSON_INT_T_PFMT, cson_value_get_integer(jseed));
980
+ anonSeed = seedBuffer;
981
+ }else if( cson_value_is_string(jseed) ){
982
+ anonSeed = cson_string_cstr(cson_value_get_string(jseed));
983
+ }
984
+ }
985
+ if(!anonSeed){
986
+ g.json.resultCode = preciseErrors
987
+ ? FSL_JSON_E_LOGIN_FAILED_NOSEED
988
+ : FSL_JSON_E_LOGIN_FAILED;
989
+ return NULL;
990
+ }
991
+ }
992
+
945993
#if 0
994
+ {
946995
/* only for debugging the PD()-incorrect-result problem */
947996
cson_object * o = NULL;
948997
uid = login_search_uid( name, pw );
949998
payload = cson_value_new_object();
950999
o = cson_value_get_object(payload);
9511000
cson_object_set( o, "n", cson_value_new_string(name,strlen(name)));
9521001
cson_object_set( o, "p", cson_value_new_string(pw,strlen(pw)));
9531002
return payload;
1003
+ }
9541004
#else
955
- uid = login_search_uid( name, pw );
956
- if( !uid ){
957
- g.json.resultCode = preciseErrors
958
- ? FSL_JSON_E_LOGIN_FAILED_NOTFOUND
959
- : FSL_JSON_E_LOGIN_FAILED;
1005
+ uid = anonSeed
1006
+ ? login_is_valid_anonymous(name, pw, anonSeed)
1007
+ : login_search_uid(name, pw)
1008
+ ;
1009
+ if( !uid ){
1010
+ g.json.resultCode = preciseErrors
1011
+ ? FSL_JSON_E_LOGIN_FAILED_NOTFOUND
1012
+ : FSL_JSON_E_LOGIN_FAILED;
1013
+ return NULL;
1014
+ }else{
1015
+ char * cookie = NULL;
1016
+ if(anonSeed){
1017
+ login_set_anon_cookie(NULL, &cookie);
9601018
}else{
961
- char * cookie = NULL;
9621019
login_set_user_cookie(name, uid, &cookie);
963
- payload = cson_value_new_string( cookie, strlen(cookie) );
964
- free(cookie);
9651020
}
1021
+ payload = cookie
1022
+ ? cson_value_new_string( cookie, strlen(cookie) )
1023
+ : cson_value_null();
1024
+ free(cookie);
9661025
return payload;
967
-#endif
9681026
}
1027
+#endif
9691028
}
9701029
9711030
/*
9721031
** Impl of /json/logout.
9731032
**
@@ -991,10 +1050,28 @@
9911050
login_clear_login_data();
9921051
g.json.authToken = NULL /* memory is owned elsewhere.*/;
9931052
}
9941053
return NULL;
9951054
}
1055
+
1056
+/*
1057
+** Implementation of the /json/anonymousPassword page.
1058
+*/
1059
+cson_value * json_page_anon_password(void){
1060
+ cson_value * v = cson_value_new_object();
1061
+ cson_object * o = cson_value_get_object(v);
1062
+ unsigned const int seed = captcha_seed();
1063
+ char const * zCaptcha = captcha_decode(seed);
1064
+ cson_object_set(o, "seed",
1065
+ cson_value_new_integer( (cson_int_t)seed )
1066
+ );
1067
+ cson_object_set(o, "captcha",
1068
+ cson_value_new_string( zCaptcha, strlen(zCaptcha) )
1069
+ );
1070
+ return v;
1071
+}
1072
+
9961073
9971074
/*
9981075
** Implementation of the /json/stat page/command.
9991076
**
10001077
*/
@@ -1006,11 +1083,10 @@
10061083
char zBuf[BufLen];
10071084
cson_value * jv = NULL;
10081085
cson_object * jo = NULL;
10091086
cson_value * jv2 = NULL;
10101087
cson_object * jo2 = NULL;
1011
- login_check_credentials();
10121088
if( !g.perm.Read ){
10131089
g.json.resultCode = FSL_JSON_E_DENIED;
10141090
return NULL;
10151091
}
10161092
#define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf)));
@@ -1116,10 +1192,11 @@
11161192
** Mapping of names to JSON pages/commands. Each name is a subpath of
11171193
** /json (in CGI mode) or a subcommand of the json command in CLI mode
11181194
*/
11191195
static const JsonPageDef JsonPageDefs[] = {
11201196
/* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */
1197
+{"anonymousPassword", json_page_anon_password, 1},
11211198
{"cap", json_page_cap, 0},
11221199
{"HAI",json_page_version,0},
11231200
{"login",json_page_login,1/*should be >0. Only 0 for dev/testing purposes.*/},
11241201
{"logout",json_page_logout,1/*should be >0. Only 0 for dev/testing purposes.*/},
11251202
{"stat",json_page_stat,0},
@@ -1156,11 +1233,11 @@
11561233
}
11571234
if( g.json.resultCode ){
11581235
json_err(g.json.resultCode, NULL, 0);
11591236
}else{
11601237
blob_zero(&buf);
1161
- root = json_response_skeleton(rc, payload, NULL);
1238
+ root = json_create_response(rc, payload, NULL);
11621239
cson_output_Blob( root, &buf, NULL );
11631240
cson_value_free(root);
11641241
cgi_set_content(&buf)/*takes ownership of the buf memory*/;
11651242
}
11661243
}
@@ -1219,11 +1296,11 @@
12191296
payload = (pageDef->func)();
12201297
}
12211298
if( g.json.resultCode ){
12221299
json_err(g.json.resultCode, NULL, 1);
12231300
}else{
1224
- payload = json_response_skeleton(rc, payload, NULL);
1301
+ payload = json_create_response(rc, payload, NULL);
12251302
cson_output_FILE( payload, stdout, &g.json.outOpt );
12261303
cson_value_free( payload );
12271304
if((0 != rc) && !g.isCGI){
12281305
/* FIXME: we need a way of passing this error back
12291306
up to the routine which called this callback.
12301307
--- src/json.c
+++ src/json.c
@@ -64,10 +64,11 @@
64 C(ASSERT,"Assertion failed");
65 C(ALLOC,"Resource allocation failed");
66 C(NYI,"Not yet implemented.");
67 C(AUTH,"Authentication error");
68 C(LOGIN_FAILED,"Login failed");
 
69 C(LOGIN_FAILED_NONAME,"Login failed - name not supplied");
70 C(LOGIN_FAILED_NOPW,"Login failed - password not supplied");
71 C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found");
72 C(MISSING_AUTH,"Authentication info missing from request");
73 C(DENIED,"Access denied");
@@ -652,11 +653,11 @@
652 ** If resultCode is non-zero and payload is not NULL then this
653 ** function calls cson_value_free(payload) and does not insert the
654 ** payload into the response.
655 **
656 */
657 cson_value * json_response_skeleton( int resultCode,
658 cson_value * payload,
659 char const * pMsg ){
660 cson_value * v = NULL;
661 cson_value * tmp = NULL;
662 cson_object * o = NULL;
@@ -713,19 +714,33 @@
713 tmp = cson_value_new_string(pMsg,strlen(pMsg));
714 SET("resultText");
715 }
716 tmp = json_getenv("requestId");
717 if( tmp ) cson_object_set( o, "requestId", tmp );
 
 
 
 
 
 
 
 
 
 
 
 
 
718 if( NULL != payload ){
719 if( resultCode ){
720 cson_value_free(payload);
721 payload = NULL;
722 }else{
723 tmp = payload;
724 SET("payload");
725 }
726 }
 
727 #undef SET
728
729 if(0){/*Only for debuggering, add some info to the response.*/
730 tmp = cson_value_new_integer( g.json.cmd.offset );
731 cson_object_set( o, "cmd.offset", tmp );
@@ -765,11 +780,11 @@
765 cson_value * resp = NULL;
766 rc = json_dumbdown_rc(rc);
767 if( rc && !msg ){
768 msg = json_err_str(rc);
769 }
770 resp = json_response_skeleton(rc, NULL, msg);
771 if( g.isCGI ){
772 Blob buf = empty_blob;
773 cgi_reset_content();
774 cson_output_Blob( resp, &buf, &g.json.outOpt );
775 cgi_set_content(&buf);
@@ -892,11 +907,11 @@
892 cson_value * json_page_login(void){
893 static char preciseErrors =
894 #if 0
895 g.json.errorDetailParanoia ? 0 : 1
896 #else
897 0
898 #endif
899 ;
900 /*
901 FIXME: we want to check the GET/POST args in this order:
902
@@ -912,10 +927,13 @@
912 checks then both forms work. Strangely enough, the
913 "p"/"password" check is not affected by this.
914 */
915 char const * name = cson_value_get_cstr(json_payload_property("name"));
916 char const * pw = NULL;
 
 
 
917 if( !name ){
918 name = PD("n",NULL);
919 if( !name ){
920 name = PD("name",NULL);
921 if( !name ){
@@ -924,11 +942,11 @@
924 : FSL_JSON_E_LOGIN_FAILED;
925 return NULL;
926 }
927 }
928 }
929
930 pw = cson_value_get_cstr(json_payload_property("password"));
931 if( !pw ){
932 pw = PD("p",NULL);
933 if( !pw ){
934 pw = PD("password",NULL);
@@ -937,37 +955,78 @@
937 if(!pw){
938 g.json.resultCode = preciseErrors
939 ? FSL_JSON_E_LOGIN_FAILED_NOPW
940 : FSL_JSON_E_LOGIN_FAILED;
941 return NULL;
942 }else{
943 cson_value * payload = NULL;
944 int uid = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
945 #if 0
 
946 /* only for debugging the PD()-incorrect-result problem */
947 cson_object * o = NULL;
948 uid = login_search_uid( name, pw );
949 payload = cson_value_new_object();
950 o = cson_value_get_object(payload);
951 cson_object_set( o, "n", cson_value_new_string(name,strlen(name)));
952 cson_object_set( o, "p", cson_value_new_string(pw,strlen(pw)));
953 return payload;
 
954 #else
955 uid = login_search_uid( name, pw );
956 if( !uid ){
957 g.json.resultCode = preciseErrors
958 ? FSL_JSON_E_LOGIN_FAILED_NOTFOUND
959 : FSL_JSON_E_LOGIN_FAILED;
 
 
 
 
 
 
 
 
960 }else{
961 char * cookie = NULL;
962 login_set_user_cookie(name, uid, &cookie);
963 payload = cson_value_new_string( cookie, strlen(cookie) );
964 free(cookie);
965 }
 
 
 
 
966 return payload;
967 #endif
968 }
 
969 }
970
971 /*
972 ** Impl of /json/logout.
973 **
@@ -991,10 +1050,28 @@
991 login_clear_login_data();
992 g.json.authToken = NULL /* memory is owned elsewhere.*/;
993 }
994 return NULL;
995 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
996
997 /*
998 ** Implementation of the /json/stat page/command.
999 **
1000 */
@@ -1006,11 +1083,10 @@
1006 char zBuf[BufLen];
1007 cson_value * jv = NULL;
1008 cson_object * jo = NULL;
1009 cson_value * jv2 = NULL;
1010 cson_object * jo2 = NULL;
1011 login_check_credentials();
1012 if( !g.perm.Read ){
1013 g.json.resultCode = FSL_JSON_E_DENIED;
1014 return NULL;
1015 }
1016 #define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf)));
@@ -1116,10 +1192,11 @@
1116 ** Mapping of names to JSON pages/commands. Each name is a subpath of
1117 ** /json (in CGI mode) or a subcommand of the json command in CLI mode
1118 */
1119 static const JsonPageDef JsonPageDefs[] = {
1120 /* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */
 
1121 {"cap", json_page_cap, 0},
1122 {"HAI",json_page_version,0},
1123 {"login",json_page_login,1/*should be >0. Only 0 for dev/testing purposes.*/},
1124 {"logout",json_page_logout,1/*should be >0. Only 0 for dev/testing purposes.*/},
1125 {"stat",json_page_stat,0},
@@ -1156,11 +1233,11 @@
1156 }
1157 if( g.json.resultCode ){
1158 json_err(g.json.resultCode, NULL, 0);
1159 }else{
1160 blob_zero(&buf);
1161 root = json_response_skeleton(rc, payload, NULL);
1162 cson_output_Blob( root, &buf, NULL );
1163 cson_value_free(root);
1164 cgi_set_content(&buf)/*takes ownership of the buf memory*/;
1165 }
1166 }
@@ -1219,11 +1296,11 @@
1219 payload = (pageDef->func)();
1220 }
1221 if( g.json.resultCode ){
1222 json_err(g.json.resultCode, NULL, 1);
1223 }else{
1224 payload = json_response_skeleton(rc, payload, NULL);
1225 cson_output_FILE( payload, stdout, &g.json.outOpt );
1226 cson_value_free( payload );
1227 if((0 != rc) && !g.isCGI){
1228 /* FIXME: we need a way of passing this error back
1229 up to the routine which called this callback.
1230
--- src/json.c
+++ src/json.c
@@ -64,10 +64,11 @@
64 C(ASSERT,"Assertion failed");
65 C(ALLOC,"Resource allocation failed");
66 C(NYI,"Not yet implemented.");
67 C(AUTH,"Authentication error");
68 C(LOGIN_FAILED,"Login failed");
69 C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed.");
70 C(LOGIN_FAILED_NONAME,"Login failed - name not supplied");
71 C(LOGIN_FAILED_NOPW,"Login failed - password not supplied");
72 C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found");
73 C(MISSING_AUTH,"Authentication info missing from request");
74 C(DENIED,"Access denied");
@@ -652,11 +653,11 @@
653 ** If resultCode is non-zero and payload is not NULL then this
654 ** function calls cson_value_free(payload) and does not insert the
655 ** payload into the response.
656 **
657 */
658 cson_value * json_create_response( int resultCode,
659 cson_value * payload,
660 char const * pMsg ){
661 cson_value * v = NULL;
662 cson_value * tmp = NULL;
663 cson_object * o = NULL;
@@ -713,19 +714,33 @@
714 tmp = cson_value_new_string(pMsg,strlen(pMsg));
715 SET("resultText");
716 }
717 tmp = json_getenv("requestId");
718 if( tmp ) cson_object_set( o, "requestId", tmp );
719
720 if(0){
721 if(g.json.cmd.v){/* this is only intended for my own testing...*/
722 tmp = g.json.cmd.v;
723 SET("$commandPath");
724 }
725 if(g.json.param.v){/* this is only intended for my own testing...*/
726 tmp = g.json.param.v;
727 SET("$params");
728 }
729 }
730
731 /* Only add the payload to SUCCESS responses. Else delete it. */
732 if( NULL != payload ){
733 if( resultCode ){
734 cson_value_free(payload);
735 payload = NULL;
736 }else{
737 tmp = payload;
738 SET("payload");
739 }
740 }
741
742 #undef SET
743
744 if(0){/*Only for debuggering, add some info to the response.*/
745 tmp = cson_value_new_integer( g.json.cmd.offset );
746 cson_object_set( o, "cmd.offset", tmp );
@@ -765,11 +780,11 @@
780 cson_value * resp = NULL;
781 rc = json_dumbdown_rc(rc);
782 if( rc && !msg ){
783 msg = json_err_str(rc);
784 }
785 resp = json_create_response(rc, NULL, msg);
786 if( g.isCGI ){
787 Blob buf = empty_blob;
788 cgi_reset_content();
789 cson_output_Blob( resp, &buf, &g.json.outOpt );
790 cgi_set_content(&buf);
@@ -892,11 +907,11 @@
907 cson_value * json_page_login(void){
908 static char preciseErrors =
909 #if 0
910 g.json.errorDetailParanoia ? 0 : 1
911 #else
912 1
913 #endif
914 ;
915 /*
916 FIXME: we want to check the GET/POST args in this order:
917
@@ -912,10 +927,13 @@
927 checks then both forms work. Strangely enough, the
928 "p"/"password" check is not affected by this.
929 */
930 char const * name = cson_value_get_cstr(json_payload_property("name"));
931 char const * pw = NULL;
932 char const * anonSeed = NULL;
933 cson_value * payload = NULL;
934 int uid = 0;
935 if( !name ){
936 name = PD("n",NULL);
937 if( !name ){
938 name = PD("name",NULL);
939 if( !name ){
@@ -924,11 +942,11 @@
942 : FSL_JSON_E_LOGIN_FAILED;
943 return NULL;
944 }
945 }
946 }
947
948 pw = cson_value_get_cstr(json_payload_property("password"));
949 if( !pw ){
950 pw = PD("p",NULL);
951 if( !pw ){
952 pw = PD("password",NULL);
@@ -937,37 +955,78 @@
955 if(!pw){
956 g.json.resultCode = preciseErrors
957 ? FSL_JSON_E_LOGIN_FAILED_NOPW
958 : FSL_JSON_E_LOGIN_FAILED;
959 return NULL;
960 }
961
962 if(0 == strcmp("anonymous",name)){
963 /* check captcha/seed values... */
964 enum { SeedBufLen = 100 /* in some JSON tests i once actually got an
965 80-digit number.
966 */
967 };
968 static char seedBuffer[SeedBufLen];
969 seedBuffer[0] = 0;
970 cson_value const * jseed = json_getenv("anonymousSeed");
971 if( !jseed ){
972 jseed = json_payload_property("anonymousSeed");
973 if( !jseed ){
974 jseed = json_getenv("cs") /* name used by HTML interface */;
975 }
976 }
977 if(jseed){
978 if( cson_value_is_number(jseed) ){
979 sprintf(seedBuffer, "%"CSON_INT_T_PFMT, cson_value_get_integer(jseed));
980 anonSeed = seedBuffer;
981 }else if( cson_value_is_string(jseed) ){
982 anonSeed = cson_string_cstr(cson_value_get_string(jseed));
983 }
984 }
985 if(!anonSeed){
986 g.json.resultCode = preciseErrors
987 ? FSL_JSON_E_LOGIN_FAILED_NOSEED
988 : FSL_JSON_E_LOGIN_FAILED;
989 return NULL;
990 }
991 }
992
993 #if 0
994 {
995 /* only for debugging the PD()-incorrect-result problem */
996 cson_object * o = NULL;
997 uid = login_search_uid( name, pw );
998 payload = cson_value_new_object();
999 o = cson_value_get_object(payload);
1000 cson_object_set( o, "n", cson_value_new_string(name,strlen(name)));
1001 cson_object_set( o, "p", cson_value_new_string(pw,strlen(pw)));
1002 return payload;
1003 }
1004 #else
1005 uid = anonSeed
1006 ? login_is_valid_anonymous(name, pw, anonSeed)
1007 : login_search_uid(name, pw)
1008 ;
1009 if( !uid ){
1010 g.json.resultCode = preciseErrors
1011 ? FSL_JSON_E_LOGIN_FAILED_NOTFOUND
1012 : FSL_JSON_E_LOGIN_FAILED;
1013 return NULL;
1014 }else{
1015 char * cookie = NULL;
1016 if(anonSeed){
1017 login_set_anon_cookie(NULL, &cookie);
1018 }else{
 
1019 login_set_user_cookie(name, uid, &cookie);
 
 
1020 }
1021 payload = cookie
1022 ? cson_value_new_string( cookie, strlen(cookie) )
1023 : cson_value_null();
1024 free(cookie);
1025 return payload;
 
1026 }
1027 #endif
1028 }
1029
1030 /*
1031 ** Impl of /json/logout.
1032 **
@@ -991,10 +1050,28 @@
1050 login_clear_login_data();
1051 g.json.authToken = NULL /* memory is owned elsewhere.*/;
1052 }
1053 return NULL;
1054 }
1055
1056 /*
1057 ** Implementation of the /json/anonymousPassword page.
1058 */
1059 cson_value * json_page_anon_password(void){
1060 cson_value * v = cson_value_new_object();
1061 cson_object * o = cson_value_get_object(v);
1062 unsigned const int seed = captcha_seed();
1063 char const * zCaptcha = captcha_decode(seed);
1064 cson_object_set(o, "seed",
1065 cson_value_new_integer( (cson_int_t)seed )
1066 );
1067 cson_object_set(o, "captcha",
1068 cson_value_new_string( zCaptcha, strlen(zCaptcha) )
1069 );
1070 return v;
1071 }
1072
1073
1074 /*
1075 ** Implementation of the /json/stat page/command.
1076 **
1077 */
@@ -1006,11 +1083,10 @@
1083 char zBuf[BufLen];
1084 cson_value * jv = NULL;
1085 cson_object * jo = NULL;
1086 cson_value * jv2 = NULL;
1087 cson_object * jo2 = NULL;
 
1088 if( !g.perm.Read ){
1089 g.json.resultCode = FSL_JSON_E_DENIED;
1090 return NULL;
1091 }
1092 #define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf)));
@@ -1116,10 +1192,11 @@
1192 ** Mapping of names to JSON pages/commands. Each name is a subpath of
1193 ** /json (in CGI mode) or a subcommand of the json command in CLI mode
1194 */
1195 static const JsonPageDef JsonPageDefs[] = {
1196 /* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */
1197 {"anonymousPassword", json_page_anon_password, 1},
1198 {"cap", json_page_cap, 0},
1199 {"HAI",json_page_version,0},
1200 {"login",json_page_login,1/*should be >0. Only 0 for dev/testing purposes.*/},
1201 {"logout",json_page_logout,1/*should be >0. Only 0 for dev/testing purposes.*/},
1202 {"stat",json_page_stat,0},
@@ -1156,11 +1233,11 @@
1233 }
1234 if( g.json.resultCode ){
1235 json_err(g.json.resultCode, NULL, 0);
1236 }else{
1237 blob_zero(&buf);
1238 root = json_create_response(rc, payload, NULL);
1239 cson_output_Blob( root, &buf, NULL );
1240 cson_value_free(root);
1241 cgi_set_content(&buf)/*takes ownership of the buf memory*/;
1242 }
1243 }
@@ -1219,11 +1296,11 @@
1296 payload = (pageDef->func)();
1297 }
1298 if( g.json.resultCode ){
1299 json_err(g.json.resultCode, NULL, 1);
1300 }else{
1301 payload = json_create_response(rc, payload, NULL);
1302 cson_output_FILE( payload, stdout, &g.json.outOpt );
1303 cson_value_free( payload );
1304 if((0 != rc) && !g.isCGI){
1305 /* FIXME: we need a way of passing this error back
1306 up to the routine which called this callback.
1307
--- src/json_detail.h
+++ src/json_detail.h
@@ -33,13 +33,14 @@
3333
FSL_JSON_E_MISSING_AUTH = FSL_JSON_E_AUTH + 1,
3434
FSL_JSON_E_DENIED = FSL_JSON_E_AUTH + 2,
3535
FSL_JSON_E_WRONG_MODE = FSL_JSON_E_AUTH + 3,
3636
3737
FSL_JSON_E_LOGIN_FAILED = FSL_JSON_E_AUTH + 100,
38
-FSL_JSON_E_LOGIN_FAILED_NONAME = FSL_JSON_E_LOGIN_FAILED + 1,
39
-FSL_JSON_E_LOGIN_FAILED_NOPW = FSL_JSON_E_LOGIN_FAILED + 2,
40
-FSL_JSON_E_LOGIN_FAILED_NOTFOUND = FSL_JSON_E_LOGIN_FAILED + 3,
38
+FSL_JSON_E_LOGIN_FAILED_NOSEED = FSL_JSON_E_LOGIN_FAILED + 1,
39
+FSL_JSON_E_LOGIN_FAILED_NONAME = FSL_JSON_E_LOGIN_FAILED + 2,
40
+FSL_JSON_E_LOGIN_FAILED_NOPW = FSL_JSON_E_LOGIN_FAILED + 3,
41
+FSL_JSON_E_LOGIN_FAILED_NOTFOUND = FSL_JSON_E_LOGIN_FAILED + 4,
4142
4243
FSL_JSON_E_USAGE = 3000,
4344
FSL_JSON_E_INVALID_ARGS = FSL_JSON_E_USAGE + 1,
4445
FSL_JSON_E_MISSING_ARGS = FSL_JSON_E_USAGE + 2,
4546
4647
--- src/json_detail.h
+++ src/json_detail.h
@@ -33,13 +33,14 @@
33 FSL_JSON_E_MISSING_AUTH = FSL_JSON_E_AUTH + 1,
34 FSL_JSON_E_DENIED = FSL_JSON_E_AUTH + 2,
35 FSL_JSON_E_WRONG_MODE = FSL_JSON_E_AUTH + 3,
36
37 FSL_JSON_E_LOGIN_FAILED = FSL_JSON_E_AUTH + 100,
38 FSL_JSON_E_LOGIN_FAILED_NONAME = FSL_JSON_E_LOGIN_FAILED + 1,
39 FSL_JSON_E_LOGIN_FAILED_NOPW = FSL_JSON_E_LOGIN_FAILED + 2,
40 FSL_JSON_E_LOGIN_FAILED_NOTFOUND = FSL_JSON_E_LOGIN_FAILED + 3,
 
41
42 FSL_JSON_E_USAGE = 3000,
43 FSL_JSON_E_INVALID_ARGS = FSL_JSON_E_USAGE + 1,
44 FSL_JSON_E_MISSING_ARGS = FSL_JSON_E_USAGE + 2,
45
46
--- src/json_detail.h
+++ src/json_detail.h
@@ -33,13 +33,14 @@
33 FSL_JSON_E_MISSING_AUTH = FSL_JSON_E_AUTH + 1,
34 FSL_JSON_E_DENIED = FSL_JSON_E_AUTH + 2,
35 FSL_JSON_E_WRONG_MODE = FSL_JSON_E_AUTH + 3,
36
37 FSL_JSON_E_LOGIN_FAILED = FSL_JSON_E_AUTH + 100,
38 FSL_JSON_E_LOGIN_FAILED_NOSEED = FSL_JSON_E_LOGIN_FAILED + 1,
39 FSL_JSON_E_LOGIN_FAILED_NONAME = FSL_JSON_E_LOGIN_FAILED + 2,
40 FSL_JSON_E_LOGIN_FAILED_NOPW = FSL_JSON_E_LOGIN_FAILED + 3,
41 FSL_JSON_E_LOGIN_FAILED_NOTFOUND = FSL_JSON_E_LOGIN_FAILED + 4,
42
43 FSL_JSON_E_USAGE = 3000,
44 FSL_JSON_E_INVALID_ARGS = FSL_JSON_E_USAGE + 1,
45 FSL_JSON_E_MISSING_ARGS = FSL_JSON_E_USAGE + 2,
46
47
+69 -44
--- src/login.c
+++ src/login.c
@@ -145,11 +145,11 @@
145145
** the userid of the anonymous user.
146146
**
147147
** The zCS parameter is the "captcha seed" used for a specific
148148
** anonymous login request.
149149
*/
150
-static int isValidAnonymousLogin(
150
+int login_is_valid_anonymous(
151151
const char *zUsername, /* The username. Must be "anonymous" */
152152
const char *zPassword, /* The supplied password */
153153
const char *zCS /* The captcha seed value */
154154
){
155155
const char *zPw; /* The correct password shown in the captcha */
@@ -274,10 +274,50 @@
274274
*zDest = zCookie;
275275
}else{
276276
free(zCookie);
277277
}
278278
}
279
+
280
+/* Sets a cookie for an anonymous user login, which looks like this:
281
+**
282
+** HASH/TIME/anonymous
283
+**
284
+** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
285
+** is the abbreviated IP address and SECRET is captcha-secret.
286
+**
287
+** If either zIpAddr or zRemoteAddr are NULL then REMOTE_ADDR
288
+** is used.
289
+**
290
+** If zCookieDest is not NULL then the generated cookie is assigned to
291
+** *zCookieDest and the caller must eventually free() it.
292
+*/
293
+void login_set_anon_cookie(char const * zIpAddr, char ** zCookieDest ){
294
+ char const *zNow; /* Current time (julian day number) */
295
+ char *zCookie; /* The login cookie */
296
+ char const *zCookieName; /* Name of the login cookie */
297
+ Blob b; /* Blob used during cookie construction */
298
+ char * zRemoteAddr; /* Abbreviated IP address */
299
+ if(!zIpAddr){
300
+ zIpAddr = PD("REMOTE_ADDR","nil");
301
+ }
302
+ zRemoteAddr = ipPrefix(zIpAddr);
303
+ zCookieName = login_cookie_name();
304
+ zNow = db_text("0", "SELECT julianday('now')");
305
+ assert( zCookieName && zRemoteAddr && zIpAddr && zNow );
306
+ blob_init(&b, zNow, -1);
307
+ blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
308
+ sha1sum_blob(&b, &b);
309
+ zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
310
+ blob_reset(&b);
311
+ cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
312
+ if( zCookieDest ){
313
+ *zCookieDest = zCookie;
314
+ }else{
315
+ free(zCookie);
316
+ }
317
+
318
+}
279319
280320
/*
281321
** "Unsets" the login cookie (insofar as cookies can be unset) and
282322
** clears the current user's (g.userUid) login information from the
283323
** user table. Sets: user.cookie, user.ipaddr, user.cexpire.
@@ -294,20 +334,19 @@
294334
char const * cookie = login_cookie_name();
295335
/* To logout, change the cookie value to an empty string */
296336
cgi_set_cookie(cookie, "",
297337
login_cookie_path(), -86400);
298338
db_multi_exec("UPDATE user SET cookie=NULL, ipaddr=NULL, "
299
- " cexpire=0 WHERE uid=%d", g.userUid);
339
+ " cexpire=0 WHERE uid=%d"
340
+ " AND login NOT IN ('anonymous','guest',"
341
+ " 'developer','reader')", g.userUid);
300342
cgi_replace_parameter(cookie, NULL)
301343
/* At the time of this writing, cgi_replace_parameter() was
302344
** "NULL-value-safe", and i'm hoping the NULL doesn't cause any
303345
** downstream problems here. We could alternately use "" here.
304346
*/
305347
;
306
- /* Potential improvement: do we want/need to skip this step for
307
- ** the guest user?
308
- */
309348
}
310349
}
311350
312351
/*
313352
** WEBPAGE: login
@@ -328,11 +367,10 @@
328367
int anonFlag;
329368
char *zErrMsg = "";
330369
int uid; /* User id loged in user */
331370
char *zSha1Pw;
332371
const char *zIpAddr; /* IP address of requestor */
333
- char *zRemoteAddr; /* Abbreviated IP address of requestor */
334372
335373
login_check_credentials();
336374
zUsername = P("u");
337375
zPasswd = P("p");
338376
anonFlag = P("anon")!=0;
@@ -383,35 +421,13 @@
383421
return;
384422
}
385423
}
386424
}
387425
zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
388
- zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */
389
- uid = isValidAnonymousLogin(zUsername, zPasswd, P("cs"));
426
+ uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs"));
390427
if( uid>0 ){
391
- /* Successful login as anonymous. Set a cookie that looks like
392
- ** this:
393
- **
394
- ** HASH/TIME/anonymous
395
- **
396
- ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
397
- ** is the abbreviated IP address and SECRET is captcha-secret.
398
- */
399
- char *zNow; /* Current time (julian day number) */
400
- char *zCookie; /* The login cookie */
401
- const char *zCookieName; /* Name of the login cookie */
402
- Blob b; /* Blob used during cookie construction */
403
-
404
- zCookieName = login_cookie_name();
405
- zNow = db_text("0", "SELECT julianday('now')");
406
- blob_init(&b, zNow, -1);
407
- blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
408
- sha1sum_blob(&b, &b);
409
- zCookie = sqlite3_mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
410
- blob_reset(&b);
411
- free(zNow);
412
- cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
428
+ login_set_anon_cookie(zIpAddr, NULL);
413429
record_login_attempt("anonymous", zIpAddr, 1);
414430
redirect_to_g();
415431
}
416432
if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
417433
/* Attempting to log in as a user other than anonymous.
@@ -600,15 +616,15 @@
600616
fossil_free(zOtherRepo);
601617
return nXfer;
602618
}
603619
604620
/*
605
-** Lookup the uid for a user with zLogin and zCookie and zRemoteAddr.
606
-** Return 0 if not found.
621
+** Lookup the uid for a non-built-in user with zLogin and zCookie and
622
+** zRemoteAddr. Return 0 if not found.
607623
**
608
-** Note that this only searches for logged-in entries with
609
-** matching zCookie (user.cookie) and zRemoteAddr (user.ipaddr)
624
+** Note that this only searches for logged-in entries with matching
625
+** zCookie (db: user.cookie) and zRemoteAddr (db: user.ipaddr)
610626
** entries.
611627
*/
612628
static int login_find_user(
613629
const char *zLogin, /* User name */
614630
const char *zCookie, /* Login cookie value */
@@ -631,16 +647,14 @@
631647
);
632648
return uid;
633649
}
634650
635651
/*
636
-** This routine examines the login cookie to see if it exists and
637
-** and is valid. If the login cookie checks out, it then sets
638
-** global variables appropriately. Global variables set include
639
-** g.userUid and g.zLogin and of the g.perm.Read family of permission
640
-** booleans.
641
-**
652
+** This routine examines the login cookie to see if it exists and and
653
+** is valid. If the login cookie checks out, it then sets global
654
+** variables appropriately. Global variables set include g.userUid
655
+** and g.zLogin and the g.perm family of permission booleans.
642656
*/
643657
void login_check_credentials(void){
644658
int uid = 0; /* User id */
645659
const char *zCookie; /* Text of the login cookie */
646660
const char *zIpAddr; /* Raw IP address of the requestor */
@@ -725,11 +739,11 @@
725739
}
726740
sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
727741
}
728742
729743
/* If no user found and the REMOTE_USER environment variable is set,
730
- ** the accept the value of REMOTE_USER as the user.
744
+ ** then accept the value of REMOTE_USER as the user.
731745
*/
732746
if( uid==0 ){
733747
const char *zRemoteUser = P("REMOTE_USER");
734748
if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){
735749
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
@@ -775,11 +789,11 @@
775789
if( fossil_strcmp(g.zLogin,"nobody")==0 ){
776790
g.zLogin = 0;
777791
}
778792
779793
/* Set the capabilities */
780
- login_set_capabilities(zCap, 0);
794
+ login_replace_capabilities(zCap, 0);
781795
login_set_anon_nobody_capabilities();
782796
}
783797
784798
/*
785799
** Memory of settings
@@ -804,22 +818,25 @@
804818
login_anon_once = 0;
805819
}
806820
}
807821
808822
/*
809
-** Flags passed into the 2nd argument of login_set_capabilities().
823
+** Flags passed into the 2nd argument of login_set/replace_capabilities().
810824
*/
811825
#if INTERFACE
812826
#define LOGIN_IGNORE_U 0x01 /* Ignore "u" */
813827
#define LOGIN_IGNORE_V 0x01 /* Ignore "v" */
814828
#endif
815829
816830
/*
817
-** Set the global capability flags based on a capability string.
831
+** Adds all capability flags in zCap to g.perm.
818832
*/
819833
void login_set_capabilities(const char *zCap, unsigned flags){
820834
int i;
835
+ if(NULL==zCap){
836
+ return;
837
+ }
821838
for(i=0; zCap[i]; i++){
822839
switch( zCap[i] ){
823840
case 's': g.perm.Setup = 1; /* Fall thru into Admin */
824841
case 'a': g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
825842
g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
@@ -873,10 +890,18 @@
873890
break;
874891
}
875892
}
876893
}
877894
}
895
+
896
+/*
897
+** Zeroes out g.perm and calls login_set_capabilities(zCap,flags).
898
+*/
899
+void login_replace_capabilities(const char *zCap, unsigned flags){
900
+ memset(&g.perm, 0, sizeof(g.perm));
901
+ return login_set_capabilities(zCap, flags);
902
+}
878903
879904
/*
880905
** If the current login lacks any of the capabilities listed in
881906
** the input, then return 0. If all capabilities are present, then
882907
** return 1.
883908
--- src/login.c
+++ src/login.c
@@ -145,11 +145,11 @@
145 ** the userid of the anonymous user.
146 **
147 ** The zCS parameter is the "captcha seed" used for a specific
148 ** anonymous login request.
149 */
150 static int isValidAnonymousLogin(
151 const char *zUsername, /* The username. Must be "anonymous" */
152 const char *zPassword, /* The supplied password */
153 const char *zCS /* The captcha seed value */
154 ){
155 const char *zPw; /* The correct password shown in the captcha */
@@ -274,10 +274,50 @@
274 *zDest = zCookie;
275 }else{
276 free(zCookie);
277 }
278 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
280 /*
281 ** "Unsets" the login cookie (insofar as cookies can be unset) and
282 ** clears the current user's (g.userUid) login information from the
283 ** user table. Sets: user.cookie, user.ipaddr, user.cexpire.
@@ -294,20 +334,19 @@
294 char const * cookie = login_cookie_name();
295 /* To logout, change the cookie value to an empty string */
296 cgi_set_cookie(cookie, "",
297 login_cookie_path(), -86400);
298 db_multi_exec("UPDATE user SET cookie=NULL, ipaddr=NULL, "
299 " cexpire=0 WHERE uid=%d", g.userUid);
 
 
300 cgi_replace_parameter(cookie, NULL)
301 /* At the time of this writing, cgi_replace_parameter() was
302 ** "NULL-value-safe", and i'm hoping the NULL doesn't cause any
303 ** downstream problems here. We could alternately use "" here.
304 */
305 ;
306 /* Potential improvement: do we want/need to skip this step for
307 ** the guest user?
308 */
309 }
310 }
311
312 /*
313 ** WEBPAGE: login
@@ -328,11 +367,10 @@
328 int anonFlag;
329 char *zErrMsg = "";
330 int uid; /* User id loged in user */
331 char *zSha1Pw;
332 const char *zIpAddr; /* IP address of requestor */
333 char *zRemoteAddr; /* Abbreviated IP address of requestor */
334
335 login_check_credentials();
336 zUsername = P("u");
337 zPasswd = P("p");
338 anonFlag = P("anon")!=0;
@@ -383,35 +421,13 @@
383 return;
384 }
385 }
386 }
387 zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
388 zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */
389 uid = isValidAnonymousLogin(zUsername, zPasswd, P("cs"));
390 if( uid>0 ){
391 /* Successful login as anonymous. Set a cookie that looks like
392 ** this:
393 **
394 ** HASH/TIME/anonymous
395 **
396 ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
397 ** is the abbreviated IP address and SECRET is captcha-secret.
398 */
399 char *zNow; /* Current time (julian day number) */
400 char *zCookie; /* The login cookie */
401 const char *zCookieName; /* Name of the login cookie */
402 Blob b; /* Blob used during cookie construction */
403
404 zCookieName = login_cookie_name();
405 zNow = db_text("0", "SELECT julianday('now')");
406 blob_init(&b, zNow, -1);
407 blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
408 sha1sum_blob(&b, &b);
409 zCookie = sqlite3_mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
410 blob_reset(&b);
411 free(zNow);
412 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
413 record_login_attempt("anonymous", zIpAddr, 1);
414 redirect_to_g();
415 }
416 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
417 /* Attempting to log in as a user other than anonymous.
@@ -600,15 +616,15 @@
600 fossil_free(zOtherRepo);
601 return nXfer;
602 }
603
604 /*
605 ** Lookup the uid for a user with zLogin and zCookie and zRemoteAddr.
606 ** Return 0 if not found.
607 **
608 ** Note that this only searches for logged-in entries with
609 ** matching zCookie (user.cookie) and zRemoteAddr (user.ipaddr)
610 ** entries.
611 */
612 static int login_find_user(
613 const char *zLogin, /* User name */
614 const char *zCookie, /* Login cookie value */
@@ -631,16 +647,14 @@
631 );
632 return uid;
633 }
634
635 /*
636 ** This routine examines the login cookie to see if it exists and
637 ** and is valid. If the login cookie checks out, it then sets
638 ** global variables appropriately. Global variables set include
639 ** g.userUid and g.zLogin and of the g.perm.Read family of permission
640 ** booleans.
641 **
642 */
643 void login_check_credentials(void){
644 int uid = 0; /* User id */
645 const char *zCookie; /* Text of the login cookie */
646 const char *zIpAddr; /* Raw IP address of the requestor */
@@ -725,11 +739,11 @@
725 }
726 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
727 }
728
729 /* If no user found and the REMOTE_USER environment variable is set,
730 ** the accept the value of REMOTE_USER as the user.
731 */
732 if( uid==0 ){
733 const char *zRemoteUser = P("REMOTE_USER");
734 if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){
735 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
@@ -775,11 +789,11 @@
775 if( fossil_strcmp(g.zLogin,"nobody")==0 ){
776 g.zLogin = 0;
777 }
778
779 /* Set the capabilities */
780 login_set_capabilities(zCap, 0);
781 login_set_anon_nobody_capabilities();
782 }
783
784 /*
785 ** Memory of settings
@@ -804,22 +818,25 @@
804 login_anon_once = 0;
805 }
806 }
807
808 /*
809 ** Flags passed into the 2nd argument of login_set_capabilities().
810 */
811 #if INTERFACE
812 #define LOGIN_IGNORE_U 0x01 /* Ignore "u" */
813 #define LOGIN_IGNORE_V 0x01 /* Ignore "v" */
814 #endif
815
816 /*
817 ** Set the global capability flags based on a capability string.
818 */
819 void login_set_capabilities(const char *zCap, unsigned flags){
820 int i;
 
 
 
821 for(i=0; zCap[i]; i++){
822 switch( zCap[i] ){
823 case 's': g.perm.Setup = 1; /* Fall thru into Admin */
824 case 'a': g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
825 g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
@@ -873,10 +890,18 @@
873 break;
874 }
875 }
876 }
877 }
 
 
 
 
 
 
 
 
878
879 /*
880 ** If the current login lacks any of the capabilities listed in
881 ** the input, then return 0. If all capabilities are present, then
882 ** return 1.
883
--- src/login.c
+++ src/login.c
@@ -145,11 +145,11 @@
145 ** the userid of the anonymous user.
146 **
147 ** The zCS parameter is the "captcha seed" used for a specific
148 ** anonymous login request.
149 */
150 int login_is_valid_anonymous(
151 const char *zUsername, /* The username. Must be "anonymous" */
152 const char *zPassword, /* The supplied password */
153 const char *zCS /* The captcha seed value */
154 ){
155 const char *zPw; /* The correct password shown in the captcha */
@@ -274,10 +274,50 @@
274 *zDest = zCookie;
275 }else{
276 free(zCookie);
277 }
278 }
279
280 /* Sets a cookie for an anonymous user login, which looks like this:
281 **
282 ** HASH/TIME/anonymous
283 **
284 ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
285 ** is the abbreviated IP address and SECRET is captcha-secret.
286 **
287 ** If either zIpAddr or zRemoteAddr are NULL then REMOTE_ADDR
288 ** is used.
289 **
290 ** If zCookieDest is not NULL then the generated cookie is assigned to
291 ** *zCookieDest and the caller must eventually free() it.
292 */
293 void login_set_anon_cookie(char const * zIpAddr, char ** zCookieDest ){
294 char const *zNow; /* Current time (julian day number) */
295 char *zCookie; /* The login cookie */
296 char const *zCookieName; /* Name of the login cookie */
297 Blob b; /* Blob used during cookie construction */
298 char * zRemoteAddr; /* Abbreviated IP address */
299 if(!zIpAddr){
300 zIpAddr = PD("REMOTE_ADDR","nil");
301 }
302 zRemoteAddr = ipPrefix(zIpAddr);
303 zCookieName = login_cookie_name();
304 zNow = db_text("0", "SELECT julianday('now')");
305 assert( zCookieName && zRemoteAddr && zIpAddr && zNow );
306 blob_init(&b, zNow, -1);
307 blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
308 sha1sum_blob(&b, &b);
309 zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
310 blob_reset(&b);
311 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
312 if( zCookieDest ){
313 *zCookieDest = zCookie;
314 }else{
315 free(zCookie);
316 }
317
318 }
319
320 /*
321 ** "Unsets" the login cookie (insofar as cookies can be unset) and
322 ** clears the current user's (g.userUid) login information from the
323 ** user table. Sets: user.cookie, user.ipaddr, user.cexpire.
@@ -294,20 +334,19 @@
334 char const * cookie = login_cookie_name();
335 /* To logout, change the cookie value to an empty string */
336 cgi_set_cookie(cookie, "",
337 login_cookie_path(), -86400);
338 db_multi_exec("UPDATE user SET cookie=NULL, ipaddr=NULL, "
339 " cexpire=0 WHERE uid=%d"
340 " AND login NOT IN ('anonymous','guest',"
341 " 'developer','reader')", g.userUid);
342 cgi_replace_parameter(cookie, NULL)
343 /* At the time of this writing, cgi_replace_parameter() was
344 ** "NULL-value-safe", and i'm hoping the NULL doesn't cause any
345 ** downstream problems here. We could alternately use "" here.
346 */
347 ;
 
 
 
348 }
349 }
350
351 /*
352 ** WEBPAGE: login
@@ -328,11 +367,10 @@
367 int anonFlag;
368 char *zErrMsg = "";
369 int uid; /* User id loged in user */
370 char *zSha1Pw;
371 const char *zIpAddr; /* IP address of requestor */
 
372
373 login_check_credentials();
374 zUsername = P("u");
375 zPasswd = P("p");
376 anonFlag = P("anon")!=0;
@@ -383,35 +421,13 @@
421 return;
422 }
423 }
424 }
425 zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
426 uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs"));
 
427 if( uid>0 ){
428 login_set_anon_cookie(zIpAddr, NULL);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429 record_login_attempt("anonymous", zIpAddr, 1);
430 redirect_to_g();
431 }
432 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
433 /* Attempting to log in as a user other than anonymous.
@@ -600,15 +616,15 @@
616 fossil_free(zOtherRepo);
617 return nXfer;
618 }
619
620 /*
621 ** Lookup the uid for a non-built-in user with zLogin and zCookie and
622 ** zRemoteAddr. Return 0 if not found.
623 **
624 ** Note that this only searches for logged-in entries with matching
625 ** zCookie (db: user.cookie) and zRemoteAddr (db: user.ipaddr)
626 ** entries.
627 */
628 static int login_find_user(
629 const char *zLogin, /* User name */
630 const char *zCookie, /* Login cookie value */
@@ -631,16 +647,14 @@
647 );
648 return uid;
649 }
650
651 /*
652 ** This routine examines the login cookie to see if it exists and and
653 ** is valid. If the login cookie checks out, it then sets global
654 ** variables appropriately. Global variables set include g.userUid
655 ** and g.zLogin and the g.perm family of permission booleans.
 
 
656 */
657 void login_check_credentials(void){
658 int uid = 0; /* User id */
659 const char *zCookie; /* Text of the login cookie */
660 const char *zIpAddr; /* Raw IP address of the requestor */
@@ -725,11 +739,11 @@
739 }
740 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
741 }
742
743 /* If no user found and the REMOTE_USER environment variable is set,
744 ** then accept the value of REMOTE_USER as the user.
745 */
746 if( uid==0 ){
747 const char *zRemoteUser = P("REMOTE_USER");
748 if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){
749 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
@@ -775,11 +789,11 @@
789 if( fossil_strcmp(g.zLogin,"nobody")==0 ){
790 g.zLogin = 0;
791 }
792
793 /* Set the capabilities */
794 login_replace_capabilities(zCap, 0);
795 login_set_anon_nobody_capabilities();
796 }
797
798 /*
799 ** Memory of settings
@@ -804,22 +818,25 @@
818 login_anon_once = 0;
819 }
820 }
821
822 /*
823 ** Flags passed into the 2nd argument of login_set/replace_capabilities().
824 */
825 #if INTERFACE
826 #define LOGIN_IGNORE_U 0x01 /* Ignore "u" */
827 #define LOGIN_IGNORE_V 0x01 /* Ignore "v" */
828 #endif
829
830 /*
831 ** Adds all capability flags in zCap to g.perm.
832 */
833 void login_set_capabilities(const char *zCap, unsigned flags){
834 int i;
835 if(NULL==zCap){
836 return;
837 }
838 for(i=0; zCap[i]; i++){
839 switch( zCap[i] ){
840 case 's': g.perm.Setup = 1; /* Fall thru into Admin */
841 case 'a': g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
842 g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
@@ -873,10 +890,18 @@
890 break;
891 }
892 }
893 }
894 }
895
896 /*
897 ** Zeroes out g.perm and calls login_set_capabilities(zCap,flags).
898 */
899 void login_replace_capabilities(const char *zCap, unsigned flags){
900 memset(&g.perm, 0, sizeof(g.perm));
901 return login_set_capabilities(zCap, flags);
902 }
903
904 /*
905 ** If the current login lacks any of the capabilities listed in
906 ** the input, then return 0. If all capabilities are present, then
907 ** return 1.
908

Keyboard Shortcuts

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