Fossil SCM
/json/user/save now accepts uid=-1 to mean create-user. Fixed a bug where when renaming a user _and_ setting his password, the password was incorrectly hashed against the old name.
Commit
fa3f629060ed45ac0edd6a7529da64e8ee15f8b9
Parent
3c69a6fd59acaea…
1 file changed
+62
-30
+62
-30
| --- src/json_user.c | ||
| +++ src/json_user.c | ||
| @@ -75,18 +75,41 @@ | ||
| 75 | 75 | json_set_err(FSL_JSON_E_UNKNOWN, |
| 76 | 76 | "Could not convert user list to JSON."); |
| 77 | 77 | } |
| 78 | 78 | return payV; |
| 79 | 79 | } |
| 80 | + | |
| 81 | +/* | |
| 82 | +** Creates a new JSON Object based on the db state of | |
| 83 | +** the given user name. On error (no record found) | |
| 84 | +** it returns NULL, else the caller owns the returned | |
| 85 | +** object. | |
| 86 | +*/ | |
| 87 | +static cson_value * json_load_user_by_name(char const * zName){ | |
| 88 | + cson_value * u = NULL; | |
| 89 | + Stmt q; | |
| 90 | + db_prepare(&q,"SELECT uid AS uid," | |
| 91 | + " login AS name," | |
| 92 | + " cap AS capabilities," | |
| 93 | + " info AS info," | |
| 94 | + " mtime AS mtime" | |
| 95 | + " FROM user" | |
| 96 | + " WHERE login=%Q", | |
| 97 | + zName); | |
| 98 | + if( (SQLITE_ROW == db_step(&q)) ){ | |
| 99 | + u = cson_sqlite3_row_to_object(q.pStmt); | |
| 100 | + } | |
| 101 | + db_finalize(&q); | |
| 102 | + return u; | |
| 103 | +} | |
| 80 | 104 | |
| 81 | 105 | /* |
| 82 | 106 | ** Impl of /json/user/get. Requires admin rights. |
| 83 | 107 | */ |
| 84 | 108 | static cson_value * json_user_get(){ |
| 85 | 109 | cson_value * payV = NULL; |
| 86 | 110 | char const * pUser = NULL; |
| 87 | - Stmt q; | |
| 88 | 111 | if(!g.perm.Admin){ |
| 89 | 112 | json_set_err(FSL_JSON_E_DENIED, |
| 90 | 113 | "Requires 'a' privileges."); |
| 91 | 114 | return NULL; |
| 92 | 115 | } |
| @@ -100,27 +123,14 @@ | ||
| 100 | 123 | } |
| 101 | 124 | if(!pUser || !*pUser){ |
| 102 | 125 | json_set_err(FSL_JSON_E_MISSING_ARGS,"Missing 'name' property."); |
| 103 | 126 | return NULL; |
| 104 | 127 | } |
| 105 | - db_prepare(&q,"SELECT uid AS uid," | |
| 106 | - " login AS name," | |
| 107 | - " cap AS capabilities," | |
| 108 | - " info AS info," | |
| 109 | - " mtime AS mtime" | |
| 110 | - " FROM user" | |
| 111 | - " WHERE login=%Q", | |
| 112 | - pUser); | |
| 113 | - if( (SQLITE_ROW == db_step(&q)) ){ | |
| 114 | - payV = cson_sqlite3_row_to_object(q.pStmt); | |
| 115 | - if(!payV){ | |
| 116 | - json_set_err(FSL_JSON_E_UNKNOWN,"Could not convert user row to JSON."); | |
| 117 | - } | |
| 118 | - }else{ | |
| 128 | + payV = json_load_user_by_name(pUser); | |
| 129 | + if(!payV){ | |
| 119 | 130 | json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,"User not found."); |
| 120 | 131 | } |
| 121 | - db_finalize(&q); | |
| 122 | 132 | return payV; |
| 123 | 133 | } |
| 124 | 134 | |
| 125 | 135 | /* |
| 126 | 136 | ** Expects pUser to contain fossil user fields in JSON form: name, |
| @@ -145,11 +155,11 @@ | ||
| 145 | 155 | ** |
| 146 | 156 | ** - Admin non-Setup users cannot change the information for Setup |
| 147 | 157 | ** users. |
| 148 | 158 | ** |
| 149 | 159 | */ |
| 150 | -int json_user_update_from_json( cson_object const * pUser ){ | |
| 160 | +int json_user_update_from_json( cson_object * pUser ){ | |
| 151 | 161 | #define CSTR(X) cson_string_cstr(cson_value_get_string( cson_object_get(pUser, X ) )) |
| 152 | 162 | char const * zName = CSTR("name"); |
| 153 | 163 | char const * zNameNew = zName; |
| 154 | 164 | char * zNameFree = NULL; |
| 155 | 165 | char const * zInfo = CSTR("info"); |
| @@ -176,17 +186,38 @@ | ||
| 176 | 186 | if(!zNameFree){ |
| 177 | 187 | return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
| 178 | 188 | "No login found for uid %d.", uid); |
| 179 | 189 | } |
| 180 | 190 | zName = zNameFree; |
| 191 | + }else if(-1==uid){ | |
| 192 | + /* try to create a new user */ | |
| 193 | + if(!g.perm.Admin && !g.perm.Setup){ | |
| 194 | + return json_set_err(FSL_JSON_E_DENIED, | |
| 195 | + "Requires 'a' or 's' privileges."); | |
| 196 | + } else if(!zName || !*zName){ | |
| 197 | + return json_set_err(FSL_JSON_E_MISSING_ARGS, | |
| 198 | + "No name specified for new user."); | |
| 199 | + }else if( db_exists("SELECT 1 FROM user WHERE login=%Q", zName) ){ | |
| 200 | + return json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS, | |
| 201 | + "User %s already exists.", zName); | |
| 202 | + }else{ | |
| 203 | + Stmt ins = empty_Stmt; | |
| 204 | + db_prepare(&ins, "INSERT INTO user (login) VALUES(%Q)",zName); | |
| 205 | + db_step( &ins ); | |
| 206 | + db_finalize(&ins); | |
| 207 | + uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); | |
| 208 | + assert(uid>0); | |
| 209 | + zNameNew = zName; | |
| 210 | + cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); | |
| 211 | + } | |
| 181 | 212 | }else{ |
| 182 | - uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", | |
| 183 | - zName); | |
| 213 | + uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); | |
| 184 | 214 | if(uid<=0){ |
| 185 | 215 | return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
| 186 | 216 | "No login found for user [%s].", zName); |
| 187 | 217 | } |
| 218 | + cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); | |
| 188 | 219 | } |
| 189 | 220 | /* |
| 190 | 221 | Todo: reserve the uid=-1 to mean that the user should be created |
| 191 | 222 | by this request. |
| 192 | 223 | |
| @@ -213,20 +244,18 @@ | ||
| 213 | 244 | } |
| 214 | 245 | |
| 215 | 246 | blob_append(&sql, "UPDATE USER SET",-1 ); |
| 216 | 247 | blob_append(&sql, " mtime=cast(strftime('%s') AS INTEGER)", -1); |
| 217 | 248 | |
| 218 | - if((uid>0) && zName){ | |
| 219 | - /* Only change the name if the uid is explicitly set and name | |
| 220 | - would actually change. */ | |
| 221 | - if( zNameNew && (zName != zNameNew) | |
| 249 | + if((uid>0) && zNameNew){ | |
| 250 | + /* Check for name change... */ | |
| 251 | + if( (!g.perm.Admin && !g.perm.Setup) | |
| 252 | + && zNameNew && (zName != zNameNew) | |
| 222 | 253 | && (0!=strcmp(zNameNew,zName))){ |
| 223 | - if(!g.perm.Admin && !g.perm.Setup) { | |
| 224 | - json_set_err( FSL_JSON_E_DENIED, | |
| 225 | - "Modifying user names requires 'a' or 's' privileges."); | |
| 226 | - goto error; | |
| 227 | - } | |
| 254 | + json_set_err( FSL_JSON_E_DENIED, | |
| 255 | + "Modifying user names requires 'a' or 's' privileges."); | |
| 256 | + goto error; | |
| 228 | 257 | } |
| 229 | 258 | forceLogout = cson_value_true() |
| 230 | 259 | /* reminders: 1) does not allocate. |
| 231 | 260 | 2) we do this because changing a name |
| 232 | 261 | invalidates any login token because the old name |
| @@ -242,11 +271,11 @@ | ||
| 242 | 271 | } |
| 243 | 272 | |
| 244 | 273 | if( zPW ){ |
| 245 | 274 | char * zPWHash = NULL; |
| 246 | 275 | ++gotFields; |
| 247 | - zPWHash = sha1_shared_secret(zPW, zName, NULL); | |
| 276 | + zPWHash = sha1_shared_secret(zPW, zNameNew ? zNameNew : zName, NULL); | |
| 248 | 277 | blob_appendf(&sql, ", pw=%Q", zPWHash); |
| 249 | 278 | free(zPWHash); |
| 250 | 279 | } |
| 251 | 280 | |
| 252 | 281 | if( zInfo ){ |
| @@ -266,11 +295,14 @@ | ||
| 266 | 295 | goto error; |
| 267 | 296 | } |
| 268 | 297 | assert(uid>0); |
| 269 | 298 | blob_appendf(&sql, " WHERE uid=%d", uid); |
| 270 | 299 | free( zNameFree ); |
| 271 | - /*puts(blob_str(&sql));*/ | |
| 300 | +#if 0 | |
| 301 | + puts(blob_str(&sql)); | |
| 302 | + cson_output_FILE( cson_object_value(pUser), stdout, NULL ); | |
| 303 | +#endif | |
| 272 | 304 | db_prepare(&q, "%s", blob_str(&sql)); |
| 273 | 305 | blob_reset(&sql); |
| 274 | 306 | db_exec(&q); |
| 275 | 307 | db_finalize(&q); |
| 276 | 308 | return 0; |
| 277 | 309 |
| --- src/json_user.c | |
| +++ src/json_user.c | |
| @@ -75,18 +75,41 @@ | |
| 75 | json_set_err(FSL_JSON_E_UNKNOWN, |
| 76 | "Could not convert user list to JSON."); |
| 77 | } |
| 78 | return payV; |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | ** Impl of /json/user/get. Requires admin rights. |
| 83 | */ |
| 84 | static cson_value * json_user_get(){ |
| 85 | cson_value * payV = NULL; |
| 86 | char const * pUser = NULL; |
| 87 | Stmt q; |
| 88 | if(!g.perm.Admin){ |
| 89 | json_set_err(FSL_JSON_E_DENIED, |
| 90 | "Requires 'a' privileges."); |
| 91 | return NULL; |
| 92 | } |
| @@ -100,27 +123,14 @@ | |
| 100 | } |
| 101 | if(!pUser || !*pUser){ |
| 102 | json_set_err(FSL_JSON_E_MISSING_ARGS,"Missing 'name' property."); |
| 103 | return NULL; |
| 104 | } |
| 105 | db_prepare(&q,"SELECT uid AS uid," |
| 106 | " login AS name," |
| 107 | " cap AS capabilities," |
| 108 | " info AS info," |
| 109 | " mtime AS mtime" |
| 110 | " FROM user" |
| 111 | " WHERE login=%Q", |
| 112 | pUser); |
| 113 | if( (SQLITE_ROW == db_step(&q)) ){ |
| 114 | payV = cson_sqlite3_row_to_object(q.pStmt); |
| 115 | if(!payV){ |
| 116 | json_set_err(FSL_JSON_E_UNKNOWN,"Could not convert user row to JSON."); |
| 117 | } |
| 118 | }else{ |
| 119 | json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,"User not found."); |
| 120 | } |
| 121 | db_finalize(&q); |
| 122 | return payV; |
| 123 | } |
| 124 | |
| 125 | /* |
| 126 | ** Expects pUser to contain fossil user fields in JSON form: name, |
| @@ -145,11 +155,11 @@ | |
| 145 | ** |
| 146 | ** - Admin non-Setup users cannot change the information for Setup |
| 147 | ** users. |
| 148 | ** |
| 149 | */ |
| 150 | int json_user_update_from_json( cson_object const * pUser ){ |
| 151 | #define CSTR(X) cson_string_cstr(cson_value_get_string( cson_object_get(pUser, X ) )) |
| 152 | char const * zName = CSTR("name"); |
| 153 | char const * zNameNew = zName; |
| 154 | char * zNameFree = NULL; |
| 155 | char const * zInfo = CSTR("info"); |
| @@ -176,17 +186,38 @@ | |
| 176 | if(!zNameFree){ |
| 177 | return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
| 178 | "No login found for uid %d.", uid); |
| 179 | } |
| 180 | zName = zNameFree; |
| 181 | }else{ |
| 182 | uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", |
| 183 | zName); |
| 184 | if(uid<=0){ |
| 185 | return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
| 186 | "No login found for user [%s].", zName); |
| 187 | } |
| 188 | } |
| 189 | /* |
| 190 | Todo: reserve the uid=-1 to mean that the user should be created |
| 191 | by this request. |
| 192 | |
| @@ -213,20 +244,18 @@ | |
| 213 | } |
| 214 | |
| 215 | blob_append(&sql, "UPDATE USER SET",-1 ); |
| 216 | blob_append(&sql, " mtime=cast(strftime('%s') AS INTEGER)", -1); |
| 217 | |
| 218 | if((uid>0) && zName){ |
| 219 | /* Only change the name if the uid is explicitly set and name |
| 220 | would actually change. */ |
| 221 | if( zNameNew && (zName != zNameNew) |
| 222 | && (0!=strcmp(zNameNew,zName))){ |
| 223 | if(!g.perm.Admin && !g.perm.Setup) { |
| 224 | json_set_err( FSL_JSON_E_DENIED, |
| 225 | "Modifying user names requires 'a' or 's' privileges."); |
| 226 | goto error; |
| 227 | } |
| 228 | } |
| 229 | forceLogout = cson_value_true() |
| 230 | /* reminders: 1) does not allocate. |
| 231 | 2) we do this because changing a name |
| 232 | invalidates any login token because the old name |
| @@ -242,11 +271,11 @@ | |
| 242 | } |
| 243 | |
| 244 | if( zPW ){ |
| 245 | char * zPWHash = NULL; |
| 246 | ++gotFields; |
| 247 | zPWHash = sha1_shared_secret(zPW, zName, NULL); |
| 248 | blob_appendf(&sql, ", pw=%Q", zPWHash); |
| 249 | free(zPWHash); |
| 250 | } |
| 251 | |
| 252 | if( zInfo ){ |
| @@ -266,11 +295,14 @@ | |
| 266 | goto error; |
| 267 | } |
| 268 | assert(uid>0); |
| 269 | blob_appendf(&sql, " WHERE uid=%d", uid); |
| 270 | free( zNameFree ); |
| 271 | /*puts(blob_str(&sql));*/ |
| 272 | db_prepare(&q, "%s", blob_str(&sql)); |
| 273 | blob_reset(&sql); |
| 274 | db_exec(&q); |
| 275 | db_finalize(&q); |
| 276 | return 0; |
| 277 |
| --- src/json_user.c | |
| +++ src/json_user.c | |
| @@ -75,18 +75,41 @@ | |
| 75 | json_set_err(FSL_JSON_E_UNKNOWN, |
| 76 | "Could not convert user list to JSON."); |
| 77 | } |
| 78 | return payV; |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | ** Creates a new JSON Object based on the db state of |
| 83 | ** the given user name. On error (no record found) |
| 84 | ** it returns NULL, else the caller owns the returned |
| 85 | ** object. |
| 86 | */ |
| 87 | static cson_value * json_load_user_by_name(char const * zName){ |
| 88 | cson_value * u = NULL; |
| 89 | Stmt q; |
| 90 | db_prepare(&q,"SELECT uid AS uid," |
| 91 | " login AS name," |
| 92 | " cap AS capabilities," |
| 93 | " info AS info," |
| 94 | " mtime AS mtime" |
| 95 | " FROM user" |
| 96 | " WHERE login=%Q", |
| 97 | zName); |
| 98 | if( (SQLITE_ROW == db_step(&q)) ){ |
| 99 | u = cson_sqlite3_row_to_object(q.pStmt); |
| 100 | } |
| 101 | db_finalize(&q); |
| 102 | return u; |
| 103 | } |
| 104 | |
| 105 | /* |
| 106 | ** Impl of /json/user/get. Requires admin rights. |
| 107 | */ |
| 108 | static cson_value * json_user_get(){ |
| 109 | cson_value * payV = NULL; |
| 110 | char const * pUser = NULL; |
| 111 | if(!g.perm.Admin){ |
| 112 | json_set_err(FSL_JSON_E_DENIED, |
| 113 | "Requires 'a' privileges."); |
| 114 | return NULL; |
| 115 | } |
| @@ -100,27 +123,14 @@ | |
| 123 | } |
| 124 | if(!pUser || !*pUser){ |
| 125 | json_set_err(FSL_JSON_E_MISSING_ARGS,"Missing 'name' property."); |
| 126 | return NULL; |
| 127 | } |
| 128 | payV = json_load_user_by_name(pUser); |
| 129 | if(!payV){ |
| 130 | json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,"User not found."); |
| 131 | } |
| 132 | return payV; |
| 133 | } |
| 134 | |
| 135 | /* |
| 136 | ** Expects pUser to contain fossil user fields in JSON form: name, |
| @@ -145,11 +155,11 @@ | |
| 155 | ** |
| 156 | ** - Admin non-Setup users cannot change the information for Setup |
| 157 | ** users. |
| 158 | ** |
| 159 | */ |
| 160 | int json_user_update_from_json( cson_object * pUser ){ |
| 161 | #define CSTR(X) cson_string_cstr(cson_value_get_string( cson_object_get(pUser, X ) )) |
| 162 | char const * zName = CSTR("name"); |
| 163 | char const * zNameNew = zName; |
| 164 | char * zNameFree = NULL; |
| 165 | char const * zInfo = CSTR("info"); |
| @@ -176,17 +186,38 @@ | |
| 186 | if(!zNameFree){ |
| 187 | return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
| 188 | "No login found for uid %d.", uid); |
| 189 | } |
| 190 | zName = zNameFree; |
| 191 | }else if(-1==uid){ |
| 192 | /* try to create a new user */ |
| 193 | if(!g.perm.Admin && !g.perm.Setup){ |
| 194 | return json_set_err(FSL_JSON_E_DENIED, |
| 195 | "Requires 'a' or 's' privileges."); |
| 196 | } else if(!zName || !*zName){ |
| 197 | return json_set_err(FSL_JSON_E_MISSING_ARGS, |
| 198 | "No name specified for new user."); |
| 199 | }else if( db_exists("SELECT 1 FROM user WHERE login=%Q", zName) ){ |
| 200 | return json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS, |
| 201 | "User %s already exists.", zName); |
| 202 | }else{ |
| 203 | Stmt ins = empty_Stmt; |
| 204 | db_prepare(&ins, "INSERT INTO user (login) VALUES(%Q)",zName); |
| 205 | db_step( &ins ); |
| 206 | db_finalize(&ins); |
| 207 | uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); |
| 208 | assert(uid>0); |
| 209 | zNameNew = zName; |
| 210 | cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); |
| 211 | } |
| 212 | }else{ |
| 213 | uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); |
| 214 | if(uid<=0){ |
| 215 | return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
| 216 | "No login found for user [%s].", zName); |
| 217 | } |
| 218 | cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); |
| 219 | } |
| 220 | /* |
| 221 | Todo: reserve the uid=-1 to mean that the user should be created |
| 222 | by this request. |
| 223 | |
| @@ -213,20 +244,18 @@ | |
| 244 | } |
| 245 | |
| 246 | blob_append(&sql, "UPDATE USER SET",-1 ); |
| 247 | blob_append(&sql, " mtime=cast(strftime('%s') AS INTEGER)", -1); |
| 248 | |
| 249 | if((uid>0) && zNameNew){ |
| 250 | /* Check for name change... */ |
| 251 | if( (!g.perm.Admin && !g.perm.Setup) |
| 252 | && zNameNew && (zName != zNameNew) |
| 253 | && (0!=strcmp(zNameNew,zName))){ |
| 254 | json_set_err( FSL_JSON_E_DENIED, |
| 255 | "Modifying user names requires 'a' or 's' privileges."); |
| 256 | goto error; |
| 257 | } |
| 258 | forceLogout = cson_value_true() |
| 259 | /* reminders: 1) does not allocate. |
| 260 | 2) we do this because changing a name |
| 261 | invalidates any login token because the old name |
| @@ -242,11 +271,11 @@ | |
| 271 | } |
| 272 | |
| 273 | if( zPW ){ |
| 274 | char * zPWHash = NULL; |
| 275 | ++gotFields; |
| 276 | zPWHash = sha1_shared_secret(zPW, zNameNew ? zNameNew : zName, NULL); |
| 277 | blob_appendf(&sql, ", pw=%Q", zPWHash); |
| 278 | free(zPWHash); |
| 279 | } |
| 280 | |
| 281 | if( zInfo ){ |
| @@ -266,11 +295,14 @@ | |
| 295 | goto error; |
| 296 | } |
| 297 | assert(uid>0); |
| 298 | blob_appendf(&sql, " WHERE uid=%d", uid); |
| 299 | free( zNameFree ); |
| 300 | #if 0 |
| 301 | puts(blob_str(&sql)); |
| 302 | cson_output_FILE( cson_object_value(pUser), stdout, NULL ); |
| 303 | #endif |
| 304 | db_prepare(&q, "%s", blob_str(&sql)); |
| 305 | blob_reset(&sql); |
| 306 | db_exec(&q); |
| 307 | db_finalize(&q); |
| 308 | return 0; |
| 309 |