Fossil SCM
Fixed a minor mem leak. Got /json/user/save mostly working.
Commit
080acb91be7b3cdc9dbab582d1ca5f87d2d0ac7f
Parent
400fe340c25f191…
2 files changed
+94
-26
+3
-1
+94
-26
| --- src/json_user.c | ||
| +++ src/json_user.c | ||
| @@ -120,24 +120,53 @@ | ||
| 120 | 120 | db_finalize(&q); |
| 121 | 121 | return payV; |
| 122 | 122 | } |
| 123 | 123 | |
| 124 | 124 | /* |
| 125 | -** Don't use - not yet finished. | |
| 125 | +** Expects pUser to contain fossil user fields in JSON form: name, | |
| 126 | +** uid, info, capabilities, password. | |
| 127 | +** | |
| 128 | +** At least one of (name, uid) must be included. All others are | |
| 129 | +** optional and their db fields will not be updated if those fields | |
| 130 | +** are not included in pUser. | |
| 131 | +** | |
| 132 | +** If uid is specified then name may refer to a _new_ name | |
| 133 | +** for a user, otherwise the name must refer to an existing user. | |
| 134 | +** | |
| 135 | +** On error g.json's error state is set one of the FSL_JSON_E_xxx | |
| 136 | +** values from FossilJsonCodes is returned. | |
| 137 | +** | |
| 138 | +** On success the db record for the given user is updated. | |
| 139 | +** | |
| 140 | +** Requires either Admin, Setup, or Password access. Non-admin/setup | |
| 141 | +** users can only change their own information. | |
| 142 | +** | |
| 143 | +** TODOs: | |
| 144 | +** | |
| 145 | +** - Admin non-Setup users cannot change the information for Setup | |
| 146 | +** users. | |
| 147 | +** | |
| 126 | 148 | */ |
| 127 | 149 | int json_user_update_from_json( cson_object const * pUser ){ |
| 128 | 150 | #define CSTR(X) cson_string_cstr(cson_value_get_string( cson_object_get(pUser, X ) )) |
| 129 | 151 | char const * zName = CSTR("name"); |
| 152 | + char const * zNameOrig = zName; | |
| 130 | 153 | char * zNameFree = NULL; |
| 131 | 154 | char const * zInfo = CSTR("info"); |
| 132 | 155 | char const * zCap = CSTR("capabilities"); |
| 133 | 156 | char const * zPW = CSTR("password"); |
| 134 | 157 | int gotFields = 0; |
| 135 | 158 | #undef CSTR |
| 136 | - cson_int_t const uid = cson_value_get_integer( cson_object_get(pUser, "uid") ); | |
| 159 | + cson_int_t uid = cson_value_get_integer( cson_object_get(pUser, "uid") ); | |
| 137 | 160 | Blob sql = empty_blob; |
| 138 | 161 | Stmt q = empty_Stmt; |
| 162 | + | |
| 163 | + if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){ | |
| 164 | + return json_set_err( FSL_JSON_E_DENIED, | |
| 165 | + "Password change requires 'a', 's', " | |
| 166 | + "or 'p' permissions."); | |
| 167 | + } | |
| 139 | 168 | |
| 140 | 169 | if(uid<=0 && (!zName||!*zName)){ |
| 141 | 170 | return json_set_err(FSL_JSON_E_MISSING_ARGS, |
| 142 | 171 | "One of 'uid' or 'name' is required."); |
| 143 | 172 | }else if(uid>0){ |
| @@ -145,52 +174,91 @@ | ||
| 145 | 174 | if(!zNameFree){ |
| 146 | 175 | return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
| 147 | 176 | "No login found for uid %d.", uid); |
| 148 | 177 | } |
| 149 | 178 | zName = zNameFree; |
| 179 | + }else{ | |
| 180 | + uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", | |
| 181 | + zName); | |
| 182 | + if(uid<=0){ | |
| 183 | + return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, | |
| 184 | + "No login found for user [%s].", zName); | |
| 185 | + } | |
| 150 | 186 | } |
| 151 | - | |
| 152 | - /* | |
| 153 | - TODO: do not allow an admin user to modify a setup user | |
| 154 | - unless the admin is also a setup user. setup.c uses | |
| 155 | - that logic. | |
| 187 | + /* Maintenance note: all error-returns from here on out should go | |
| 188 | + via goto error in order to clean up. | |
| 156 | 189 | */ |
| 157 | 190 | |
| 158 | - blob_append(&sql, "UPDATE USER SET ",-1 ); | |
| 159 | - if( zInfo ){ | |
| 160 | - blob_appendf(&sql, " info=%Q", zInfo); | |
| 191 | + if(uid != g.userUid){ | |
| 192 | + /* | |
| 193 | + TODO: do not allow an admin user to modify a setup user | |
| 194 | + unless the admin is also a setup user. setup.c uses | |
| 195 | + that logic. | |
| 196 | + */ | |
| 197 | + if(!g.perm.Admin && !g.perm.Setup){ | |
| 198 | + json_set_err(FSL_JSON_E_DENIED, | |
| 199 | + "Changing another user's data requires " | |
| 200 | + "'a' or 's' privileges."); | |
| 201 | + } | |
| 202 | + } | |
| 203 | + | |
| 204 | + blob_append(&sql, "UPDATE USER SET",-1 ); | |
| 205 | + blob_append(&sql, " mtime=cast(strftime('%s') AS INTEGER)", -1); | |
| 206 | + | |
| 207 | + if((uid>0) && zName | |
| 208 | + && zNameOrig && (zName != zNameOrig) | |
| 209 | + && (0!=strcmp(zNameOrig,zName))){ | |
| 210 | + /* Only change the name if the uid is explicitly set and name | |
| 211 | + would actually change. */ | |
| 212 | + if(!g.perm.Admin && !g.perm.Setup) { | |
| 213 | + json_set_err( FSL_JSON_E_DENIED, | |
| 214 | + "Modifying user names requires 'a' or 's' privileges."); | |
| 215 | + goto error; | |
| 216 | + } | |
| 217 | + blob_appendf(&sql, ", login=%Q", zNameOrig); | |
| 161 | 218 | ++gotFields; |
| 162 | 219 | } |
| 220 | + | |
| 163 | 221 | if( zCap ){ |
| 164 | - blob_appendf(&sql, "%c cap=%Q", (gotFields ? ',' : ' '), zCap); | |
| 222 | + blob_appendf(&sql, ", cap=%Q", zCap); | |
| 165 | 223 | ++gotFields; |
| 166 | 224 | } |
| 225 | + | |
| 167 | 226 | if( zPW ){ |
| 168 | - assert( zName != NULL); | |
| 169 | - blob_appendf(&sql, "%c password=coalesce(shared_secret(%Q,%Q," | |
| 170 | - "(SELECT value FROM config WHERE name='project-code')),pw),", | |
| 171 | - (gotFields ? ',' : ' '), zPW, zName); | |
| 227 | + char * zPWHash = NULL; | |
| 228 | + ++gotFields; | |
| 229 | + zPWHash = sha1_shared_secret(zPW, zName, NULL); | |
| 230 | + blob_appendf(&sql, ", pw=%Q", zPWHash); | |
| 231 | + free(zPWHash); | |
| 232 | + } | |
| 233 | + | |
| 234 | + if( zInfo ){ | |
| 235 | + blob_appendf(&sql, ", info=%Q", zInfo); | |
| 172 | 236 | ++gotFields; |
| 173 | 237 | } |
| 174 | 238 | |
| 175 | 239 | if(!gotFields){ |
| 176 | - free( zNameFree ); | |
| 177 | - blob_reset(&sql); | |
| 178 | - return FSL_JSON_E_MISSING_ARGS; | |
| 240 | + json_set_err( FSL_JSON_E_MISSING_ARGS, | |
| 241 | + "Required user data are missing."); | |
| 242 | + goto error; | |
| 179 | 243 | } |
| 180 | 244 | blob_append(&sql, " WHERE", -1); |
| 181 | - if(uid>0){ | |
| 182 | - blob_appendf(&sql, " uid=%d", uid); | |
| 183 | - }else{ | |
| 184 | - blob_appendf(&sql, " login=%Q", zName); | |
| 185 | - } | |
| 245 | + assert(uid>0); | |
| 246 | + blob_appendf(&sql, " uid=%d", uid); | |
| 186 | 247 | free( zNameFree ); |
| 187 | - | |
| 188 | - puts(blob_str(&sql)); | |
| 248 | + /*puts(blob_str(&sql));*/ | |
| 249 | + db_prepare(&q, "%s", blob_str(&sql)); | |
| 189 | 250 | blob_reset(&sql); |
| 190 | - assert(0 && "This is going to require extra work for login groups."); | |
| 251 | + db_exec(&q); | |
| 252 | + db_finalize(&q); | |
| 191 | 253 | return 0; |
| 254 | + | |
| 255 | + error: | |
| 256 | + assert(0 != g.json.resultCode); | |
| 257 | + free(zNameFree); | |
| 258 | + blob_reset(&sql); | |
| 259 | + return g.json.resultCode; | |
| 192 | 260 | } |
| 193 | 261 | |
| 194 | 262 | |
| 195 | 263 | /* |
| 196 | 264 | ** Don't use - not yet finished. |
| 197 | 265 |
| --- src/json_user.c | |
| +++ src/json_user.c | |
| @@ -120,24 +120,53 @@ | |
| 120 | db_finalize(&q); |
| 121 | return payV; |
| 122 | } |
| 123 | |
| 124 | /* |
| 125 | ** Don't use - not yet finished. |
| 126 | */ |
| 127 | int json_user_update_from_json( cson_object const * pUser ){ |
| 128 | #define CSTR(X) cson_string_cstr(cson_value_get_string( cson_object_get(pUser, X ) )) |
| 129 | char const * zName = CSTR("name"); |
| 130 | char * zNameFree = NULL; |
| 131 | char const * zInfo = CSTR("info"); |
| 132 | char const * zCap = CSTR("capabilities"); |
| 133 | char const * zPW = CSTR("password"); |
| 134 | int gotFields = 0; |
| 135 | #undef CSTR |
| 136 | cson_int_t const uid = cson_value_get_integer( cson_object_get(pUser, "uid") ); |
| 137 | Blob sql = empty_blob; |
| 138 | Stmt q = empty_Stmt; |
| 139 | |
| 140 | if(uid<=0 && (!zName||!*zName)){ |
| 141 | return json_set_err(FSL_JSON_E_MISSING_ARGS, |
| 142 | "One of 'uid' or 'name' is required."); |
| 143 | }else if(uid>0){ |
| @@ -145,52 +174,91 @@ | |
| 145 | if(!zNameFree){ |
| 146 | return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
| 147 | "No login found for uid %d.", uid); |
| 148 | } |
| 149 | zName = zNameFree; |
| 150 | } |
| 151 | |
| 152 | /* |
| 153 | TODO: do not allow an admin user to modify a setup user |
| 154 | unless the admin is also a setup user. setup.c uses |
| 155 | that logic. |
| 156 | */ |
| 157 | |
| 158 | blob_append(&sql, "UPDATE USER SET ",-1 ); |
| 159 | if( zInfo ){ |
| 160 | blob_appendf(&sql, " info=%Q", zInfo); |
| 161 | ++gotFields; |
| 162 | } |
| 163 | if( zCap ){ |
| 164 | blob_appendf(&sql, "%c cap=%Q", (gotFields ? ',' : ' '), zCap); |
| 165 | ++gotFields; |
| 166 | } |
| 167 | if( zPW ){ |
| 168 | assert( zName != NULL); |
| 169 | blob_appendf(&sql, "%c password=coalesce(shared_secret(%Q,%Q," |
| 170 | "(SELECT value FROM config WHERE name='project-code')),pw),", |
| 171 | (gotFields ? ',' : ' '), zPW, zName); |
| 172 | ++gotFields; |
| 173 | } |
| 174 | |
| 175 | if(!gotFields){ |
| 176 | free( zNameFree ); |
| 177 | blob_reset(&sql); |
| 178 | return FSL_JSON_E_MISSING_ARGS; |
| 179 | } |
| 180 | blob_append(&sql, " WHERE", -1); |
| 181 | if(uid>0){ |
| 182 | blob_appendf(&sql, " uid=%d", uid); |
| 183 | }else{ |
| 184 | blob_appendf(&sql, " login=%Q", zName); |
| 185 | } |
| 186 | free( zNameFree ); |
| 187 | |
| 188 | puts(blob_str(&sql)); |
| 189 | blob_reset(&sql); |
| 190 | assert(0 && "This is going to require extra work for login groups."); |
| 191 | return 0; |
| 192 | } |
| 193 | |
| 194 | |
| 195 | /* |
| 196 | ** Don't use - not yet finished. |
| 197 |
| --- src/json_user.c | |
| +++ src/json_user.c | |
| @@ -120,24 +120,53 @@ | |
| 120 | db_finalize(&q); |
| 121 | return payV; |
| 122 | } |
| 123 | |
| 124 | /* |
| 125 | ** Expects pUser to contain fossil user fields in JSON form: name, |
| 126 | ** uid, info, capabilities, password. |
| 127 | ** |
| 128 | ** At least one of (name, uid) must be included. All others are |
| 129 | ** optional and their db fields will not be updated if those fields |
| 130 | ** are not included in pUser. |
| 131 | ** |
| 132 | ** If uid is specified then name may refer to a _new_ name |
| 133 | ** for a user, otherwise the name must refer to an existing user. |
| 134 | ** |
| 135 | ** On error g.json's error state is set one of the FSL_JSON_E_xxx |
| 136 | ** values from FossilJsonCodes is returned. |
| 137 | ** |
| 138 | ** On success the db record for the given user is updated. |
| 139 | ** |
| 140 | ** Requires either Admin, Setup, or Password access. Non-admin/setup |
| 141 | ** users can only change their own information. |
| 142 | ** |
| 143 | ** TODOs: |
| 144 | ** |
| 145 | ** - Admin non-Setup users cannot change the information for Setup |
| 146 | ** users. |
| 147 | ** |
| 148 | */ |
| 149 | int json_user_update_from_json( cson_object const * pUser ){ |
| 150 | #define CSTR(X) cson_string_cstr(cson_value_get_string( cson_object_get(pUser, X ) )) |
| 151 | char const * zName = CSTR("name"); |
| 152 | char const * zNameOrig = zName; |
| 153 | char * zNameFree = NULL; |
| 154 | char const * zInfo = CSTR("info"); |
| 155 | char const * zCap = CSTR("capabilities"); |
| 156 | char const * zPW = CSTR("password"); |
| 157 | int gotFields = 0; |
| 158 | #undef CSTR |
| 159 | cson_int_t uid = cson_value_get_integer( cson_object_get(pUser, "uid") ); |
| 160 | Blob sql = empty_blob; |
| 161 | Stmt q = empty_Stmt; |
| 162 | |
| 163 | if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){ |
| 164 | return json_set_err( FSL_JSON_E_DENIED, |
| 165 | "Password change requires 'a', 's', " |
| 166 | "or 'p' permissions."); |
| 167 | } |
| 168 | |
| 169 | if(uid<=0 && (!zName||!*zName)){ |
| 170 | return json_set_err(FSL_JSON_E_MISSING_ARGS, |
| 171 | "One of 'uid' or 'name' is required."); |
| 172 | }else if(uid>0){ |
| @@ -145,52 +174,91 @@ | |
| 174 | if(!zNameFree){ |
| 175 | return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
| 176 | "No login found for uid %d.", uid); |
| 177 | } |
| 178 | zName = zNameFree; |
| 179 | }else{ |
| 180 | uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", |
| 181 | zName); |
| 182 | if(uid<=0){ |
| 183 | return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
| 184 | "No login found for user [%s].", zName); |
| 185 | } |
| 186 | } |
| 187 | /* Maintenance note: all error-returns from here on out should go |
| 188 | via goto error in order to clean up. |
| 189 | */ |
| 190 | |
| 191 | if(uid != g.userUid){ |
| 192 | /* |
| 193 | TODO: do not allow an admin user to modify a setup user |
| 194 | unless the admin is also a setup user. setup.c uses |
| 195 | that logic. |
| 196 | */ |
| 197 | if(!g.perm.Admin && !g.perm.Setup){ |
| 198 | json_set_err(FSL_JSON_E_DENIED, |
| 199 | "Changing another user's data requires " |
| 200 | "'a' or 's' privileges."); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | blob_append(&sql, "UPDATE USER SET",-1 ); |
| 205 | blob_append(&sql, " mtime=cast(strftime('%s') AS INTEGER)", -1); |
| 206 | |
| 207 | if((uid>0) && zName |
| 208 | && zNameOrig && (zName != zNameOrig) |
| 209 | && (0!=strcmp(zNameOrig,zName))){ |
| 210 | /* Only change the name if the uid is explicitly set and name |
| 211 | would actually change. */ |
| 212 | if(!g.perm.Admin && !g.perm.Setup) { |
| 213 | json_set_err( FSL_JSON_E_DENIED, |
| 214 | "Modifying user names requires 'a' or 's' privileges."); |
| 215 | goto error; |
| 216 | } |
| 217 | blob_appendf(&sql, ", login=%Q", zNameOrig); |
| 218 | ++gotFields; |
| 219 | } |
| 220 | |
| 221 | if( zCap ){ |
| 222 | blob_appendf(&sql, ", cap=%Q", zCap); |
| 223 | ++gotFields; |
| 224 | } |
| 225 | |
| 226 | if( zPW ){ |
| 227 | char * zPWHash = NULL; |
| 228 | ++gotFields; |
| 229 | zPWHash = sha1_shared_secret(zPW, zName, NULL); |
| 230 | blob_appendf(&sql, ", pw=%Q", zPWHash); |
| 231 | free(zPWHash); |
| 232 | } |
| 233 | |
| 234 | if( zInfo ){ |
| 235 | blob_appendf(&sql, ", info=%Q", zInfo); |
| 236 | ++gotFields; |
| 237 | } |
| 238 | |
| 239 | if(!gotFields){ |
| 240 | json_set_err( FSL_JSON_E_MISSING_ARGS, |
| 241 | "Required user data are missing."); |
| 242 | goto error; |
| 243 | } |
| 244 | blob_append(&sql, " WHERE", -1); |
| 245 | assert(uid>0); |
| 246 | blob_appendf(&sql, " uid=%d", uid); |
| 247 | free( zNameFree ); |
| 248 | /*puts(blob_str(&sql));*/ |
| 249 | db_prepare(&q, "%s", blob_str(&sql)); |
| 250 | blob_reset(&sql); |
| 251 | db_exec(&q); |
| 252 | db_finalize(&q); |
| 253 | return 0; |
| 254 | |
| 255 | error: |
| 256 | assert(0 != g.json.resultCode); |
| 257 | free(zNameFree); |
| 258 | blob_reset(&sql); |
| 259 | return g.json.resultCode; |
| 260 | } |
| 261 | |
| 262 | |
| 263 | /* |
| 264 | ** Don't use - not yet finished. |
| 265 |
+3
-1
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -227,11 +227,13 @@ | ||
| 227 | 227 | ** subsequently stored in user.cookie for later validation. |
| 228 | 228 | ** |
| 229 | 229 | ** The returned memory should be free()d after use. |
| 230 | 230 | */ |
| 231 | 231 | char * login_gen_user_cookie_value(char const *zUsername, char const * zHash){ |
| 232 | - char *zCode = abbreviated_project_code(db_get("project-code","")); | |
| 232 | + char * zProjCode = db_get("project-code",NULL); | |
| 233 | + char *zCode = abbreviated_project_code(zProjCode); | |
| 234 | + free(zProjCode); | |
| 233 | 235 | assert((zUsername && *zUsername) && "Invalid user data."); |
| 234 | 236 | return mprintf("%s/%z/%s", zHash, zCode, zUsername); |
| 235 | 237 | } |
| 236 | 238 | |
| 237 | 239 | /* |
| 238 | 240 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -227,11 +227,13 @@ | |
| 227 | ** subsequently stored in user.cookie for later validation. |
| 228 | ** |
| 229 | ** The returned memory should be free()d after use. |
| 230 | */ |
| 231 | char * login_gen_user_cookie_value(char const *zUsername, char const * zHash){ |
| 232 | char *zCode = abbreviated_project_code(db_get("project-code","")); |
| 233 | assert((zUsername && *zUsername) && "Invalid user data."); |
| 234 | return mprintf("%s/%z/%s", zHash, zCode, zUsername); |
| 235 | } |
| 236 | |
| 237 | /* |
| 238 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -227,11 +227,13 @@ | |
| 227 | ** subsequently stored in user.cookie for later validation. |
| 228 | ** |
| 229 | ** The returned memory should be free()d after use. |
| 230 | */ |
| 231 | char * login_gen_user_cookie_value(char const *zUsername, char const * zHash){ |
| 232 | char * zProjCode = db_get("project-code",NULL); |
| 233 | char *zCode = abbreviated_project_code(zProjCode); |
| 234 | free(zProjCode); |
| 235 | assert((zUsername && *zUsername) && "Invalid user data."); |
| 236 | return mprintf("%s/%z/%s", zHash, zCode, zUsername); |
| 237 | } |
| 238 | |
| 239 | /* |
| 240 |