Fossil SCM
More checking of user permissions on the Security Audit page.
Commit
564e42dfe577d60983345042dea1d358f080151e31afa2edf366e51d96d10017
Parent
8fe2f97ea1bda4d…
2 files changed
+61
-7
+63
-48
+61
-7
| --- src/security_audit.c | ||
| +++ src/security_audit.c | ||
| @@ -187,31 +187,85 @@ | ||
| 187 | 187 | @ Anonymous users can create or edit wiki, but moderator |
| 188 | 188 | @ approval is required before the edits become permanent. |
| 189 | 189 | } |
| 190 | 190 | } |
| 191 | 191 | |
| 192 | - /* The push-unversioned privilege should only be provided to | |
| 192 | + /* Administrative privilege should only be provided to | |
| 193 | 193 | ** specific individuals, not to entire classes of people. |
| 194 | + ** And not too many people should have administrator privilege. | |
| 194 | 195 | */ |
| 195 | 196 | z = db_text(0, "SELECT group_concat(login,' AND ') FROM user" |
| 196 | - " WHERE cap GLOB '*y*'" | |
| 197 | + " WHERE cap GLOB '*[as]*'" | |
| 197 | 198 | " AND login in ('anonymous','nobody','reader','developer')"); |
| 198 | 199 | if( z && z[0] ){ |
| 199 | 200 | @ <li><p> |
| 200 | - @ The "Write-Unver" privilege is granted to an entire class of users | |
| 201 | + @ Adminstrative privilege is granted to an entire class of users | |
| 201 | 202 | @ (%h(z)). Ideally, the Write-Unver privilege should only be |
| 202 | 203 | @ granted to specific individuals. |
| 203 | 204 | } |
| 205 | + n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*[as]*'"); | |
| 206 | + if( n==0 ){ | |
| 207 | + @ <li><p> | |
| 208 | + @ No users have administrator privilege. | |
| 209 | + }else{ | |
| 210 | + z = db_text(0, | |
| 211 | + "SELECT group_concat(" | |
| 212 | + "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," | |
| 213 | + "', ')" | |
| 214 | + " FROM user" | |
| 215 | + " WHERE cap GLOB '*[as]*'" | |
| 216 | + ); | |
| 217 | + @ <li><p> | |
| 218 | + @ Users with administrator privilege are: %s(z) | |
| 219 | + fossil_free(z); | |
| 220 | + if( n>3 ){ | |
| 221 | + @ <p><b>Caution</b>: | |
| 222 | + @ Administrator privilege is granted to | |
| 223 | + @ <a href='setup_ulist?with=as'>%d(n) users</a>. | |
| 224 | + @ Ideally, administator privilege ('s' or 'a') should only | |
| 225 | + @ be granted to one or two users. | |
| 226 | + } | |
| 227 | + } | |
| 204 | 228 | |
| 205 | - /* Check to see if push-unversioned is granted to many people. | |
| 229 | + /* The push-unversioned privilege should only be provided to | |
| 230 | + ** specific individuals, not to entire classes of people. | |
| 231 | + ** And no too many people should have this privilege. | |
| 206 | 232 | */ |
| 233 | + z = db_text(0, | |
| 234 | + "SELECT group_concat(" | |
| 235 | + "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," | |
| 236 | + "' and ')" | |
| 237 | + " FROM user" | |
| 238 | + " WHERE cap GLOB '*y*'" | |
| 239 | + " AND login in ('anonymous','nobody','reader','developer')" | |
| 240 | + ); | |
| 241 | + if( z && z[0] ){ | |
| 242 | + @ <li><p> | |
| 243 | + @ The "Write-Unver" privilege is granted to an entire class of users | |
| 244 | + @ (%s(z)). Ideally, the Write-Unver privilege should only be | |
| 245 | + @ granted to specific individuals. | |
| 246 | + fossil_free(z); | |
| 247 | + } | |
| 207 | 248 | n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*y*'"); |
| 208 | 249 | if( n>3 ){ |
| 209 | 250 | @ <li><p> |
| 210 | - @ The "Write-Unver" privilege is granted to %d(n) users. | |
| 211 | - @ Ideally, the Write-Unver privilege should only | |
| 212 | - @ be granted to one or two users. | |
| 251 | + }else if( n>0 ){ | |
| 252 | + z = db_text(0, | |
| 253 | + "SELECT group_concat(" | |
| 254 | + "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),', ')" | |
| 255 | + " FROM user WHERE cap GLOB '*y*'" | |
| 256 | + ); | |
| 257 | + @ <li><p> | |
| 258 | + @ Users with "Write-Unver" privilege: %s(z) | |
| 259 | + fossil_free(z); | |
| 260 | + if( n>3 ){ | |
| 261 | + @ <p><b>Caution:</b> | |
| 262 | + @ The "Write-Unver" privilege ('y') is granted to an excessive | |
| 263 | + @ number of users (%d(n)). | |
| 264 | + @ Ideally, the Write-Unver privilege should only | |
| 265 | + @ be granted to one or two users. | |
| 266 | + } | |
| 213 | 267 | } |
| 214 | 268 | |
| 215 | 269 | /* Notify if REMOTE_USER or HTTP_AUTHENTICATION is used for login. |
| 216 | 270 | */ |
| 217 | 271 | if( db_get_boolean("remote_user_ok", 0) ){ |
| 218 | 272 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -187,31 +187,85 @@ | |
| 187 | @ Anonymous users can create or edit wiki, but moderator |
| 188 | @ approval is required before the edits become permanent. |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | /* The push-unversioned privilege should only be provided to |
| 193 | ** specific individuals, not to entire classes of people. |
| 194 | */ |
| 195 | z = db_text(0, "SELECT group_concat(login,' AND ') FROM user" |
| 196 | " WHERE cap GLOB '*y*'" |
| 197 | " AND login in ('anonymous','nobody','reader','developer')"); |
| 198 | if( z && z[0] ){ |
| 199 | @ <li><p> |
| 200 | @ The "Write-Unver" privilege is granted to an entire class of users |
| 201 | @ (%h(z)). Ideally, the Write-Unver privilege should only be |
| 202 | @ granted to specific individuals. |
| 203 | } |
| 204 | |
| 205 | /* Check to see if push-unversioned is granted to many people. |
| 206 | */ |
| 207 | n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*y*'"); |
| 208 | if( n>3 ){ |
| 209 | @ <li><p> |
| 210 | @ The "Write-Unver" privilege is granted to %d(n) users. |
| 211 | @ Ideally, the Write-Unver privilege should only |
| 212 | @ be granted to one or two users. |
| 213 | } |
| 214 | |
| 215 | /* Notify if REMOTE_USER or HTTP_AUTHENTICATION is used for login. |
| 216 | */ |
| 217 | if( db_get_boolean("remote_user_ok", 0) ){ |
| 218 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -187,31 +187,85 @@ | |
| 187 | @ Anonymous users can create or edit wiki, but moderator |
| 188 | @ approval is required before the edits become permanent. |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | /* Administrative privilege should only be provided to |
| 193 | ** specific individuals, not to entire classes of people. |
| 194 | ** And not too many people should have administrator privilege. |
| 195 | */ |
| 196 | z = db_text(0, "SELECT group_concat(login,' AND ') FROM user" |
| 197 | " WHERE cap GLOB '*[as]*'" |
| 198 | " AND login in ('anonymous','nobody','reader','developer')"); |
| 199 | if( z && z[0] ){ |
| 200 | @ <li><p> |
| 201 | @ Adminstrative privilege is granted to an entire class of users |
| 202 | @ (%h(z)). Ideally, the Write-Unver privilege should only be |
| 203 | @ granted to specific individuals. |
| 204 | } |
| 205 | n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*[as]*'"); |
| 206 | if( n==0 ){ |
| 207 | @ <li><p> |
| 208 | @ No users have administrator privilege. |
| 209 | }else{ |
| 210 | z = db_text(0, |
| 211 | "SELECT group_concat(" |
| 212 | "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," |
| 213 | "', ')" |
| 214 | " FROM user" |
| 215 | " WHERE cap GLOB '*[as]*'" |
| 216 | ); |
| 217 | @ <li><p> |
| 218 | @ Users with administrator privilege are: %s(z) |
| 219 | fossil_free(z); |
| 220 | if( n>3 ){ |
| 221 | @ <p><b>Caution</b>: |
| 222 | @ Administrator privilege is granted to |
| 223 | @ <a href='setup_ulist?with=as'>%d(n) users</a>. |
| 224 | @ Ideally, administator privilege ('s' or 'a') should only |
| 225 | @ be granted to one or two users. |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | /* The push-unversioned privilege should only be provided to |
| 230 | ** specific individuals, not to entire classes of people. |
| 231 | ** And no too many people should have this privilege. |
| 232 | */ |
| 233 | z = db_text(0, |
| 234 | "SELECT group_concat(" |
| 235 | "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," |
| 236 | "' and ')" |
| 237 | " FROM user" |
| 238 | " WHERE cap GLOB '*y*'" |
| 239 | " AND login in ('anonymous','nobody','reader','developer')" |
| 240 | ); |
| 241 | if( z && z[0] ){ |
| 242 | @ <li><p> |
| 243 | @ The "Write-Unver" privilege is granted to an entire class of users |
| 244 | @ (%s(z)). Ideally, the Write-Unver privilege should only be |
| 245 | @ granted to specific individuals. |
| 246 | fossil_free(z); |
| 247 | } |
| 248 | n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*y*'"); |
| 249 | if( n>3 ){ |
| 250 | @ <li><p> |
| 251 | }else if( n>0 ){ |
| 252 | z = db_text(0, |
| 253 | "SELECT group_concat(" |
| 254 | "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),', ')" |
| 255 | " FROM user WHERE cap GLOB '*y*'" |
| 256 | ); |
| 257 | @ <li><p> |
| 258 | @ Users with "Write-Unver" privilege: %s(z) |
| 259 | fossil_free(z); |
| 260 | if( n>3 ){ |
| 261 | @ <p><b>Caution:</b> |
| 262 | @ The "Write-Unver" privilege ('y') is granted to an excessive |
| 263 | @ number of users (%d(n)). |
| 264 | @ Ideally, the Write-Unver privilege should only |
| 265 | @ be granted to one or two users. |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | /* Notify if REMOTE_USER or HTTP_AUTHENTICATION is used for login. |
| 270 | */ |
| 271 | if( db_get_boolean("remote_user_ok", 0) ){ |
| 272 |
+63
-48
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -139,66 +139,75 @@ | ||
| 139 | 139 | /* |
| 140 | 140 | ** WEBPAGE: setup_ulist |
| 141 | 141 | ** |
| 142 | 142 | ** Show a list of users. Clicking on any user jumps to the edit |
| 143 | 143 | ** screen for that user. Requires Admin privileges. |
| 144 | +** | |
| 145 | +** Query parameters: | |
| 146 | +** | |
| 147 | +** with=CAP Only show users that have one or more capabilities in CAP. | |
| 144 | 148 | */ |
| 145 | 149 | void setup_ulist(void){ |
| 146 | 150 | Stmt s; |
| 147 | 151 | double rNow; |
| 152 | + const char *zWith = P("with"); | |
| 148 | 153 | |
| 149 | 154 | login_check_credentials(); |
| 150 | 155 | if( !g.perm.Admin ){ |
| 151 | 156 | login_needed(0); |
| 152 | 157 | return; |
| 153 | 158 | } |
| 154 | 159 | |
| 155 | - style_submenu_element("Add", "setup_uedit"); | |
| 156 | - style_submenu_element("Log", "access_log"); | |
| 157 | - style_submenu_element("Help", "setup_ulist_notes"); | |
| 158 | - style_header("User List"); | |
| 159 | - @ <table border=1 cellpadding=2 cellspacing=0 class='userTable'> | |
| 160 | - @ <thead><tr> | |
| 161 | - @ <th>UID <th>Category | |
| 162 | - @ <th>Capabilities (<a href='%R/setup_ucap_list'>key</a>) | |
| 163 | - @ <th>Info <th>Last Change</tr></thead> | |
| 164 | - @ <tbody> | |
| 165 | - db_prepare(&s, | |
| 166 | - "SELECT uid, login, cap, date(mtime,'unixepoch')" | |
| 167 | - " FROM user" | |
| 168 | - " WHERE login IN ('anonymous','nobody','developer','reader')" | |
| 169 | - " ORDER BY login" | |
| 170 | - ); | |
| 171 | - while( db_step(&s)==SQLITE_ROW ){ | |
| 172 | - int uid = db_column_int(&s, 0); | |
| 173 | - const char *zLogin = db_column_text(&s, 1); | |
| 174 | - const char *zCap = db_column_text(&s, 2); | |
| 175 | - const char *zDate = db_column_text(&s, 4); | |
| 176 | - @ <tr> | |
| 177 | - @ <td><a href='setup_uedit?id=%d(uid)'>%d(uid)</a> | |
| 178 | - @ <td><a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a> | |
| 179 | - @ <td>%h(zCap) | |
| 180 | - | |
| 181 | - if( fossil_strcmp(zLogin,"anonymous")==0 ){ | |
| 182 | - @ <td>All logged-in users | |
| 183 | - }else if( fossil_strcmp(zLogin,"developer")==0 ){ | |
| 184 | - @ <td>Users with '<b>v</b>' capability | |
| 185 | - }else if( fossil_strcmp(zLogin,"nobody")==0 ){ | |
| 186 | - @ <td>All users without login | |
| 187 | - }else if( fossil_strcmp(zLogin,"reader")==0 ){ | |
| 188 | - @ <td>Users with '<b>u</b>' capability | |
| 189 | - }else{ | |
| 190 | - @ <td> | |
| 191 | - } | |
| 192 | - if( zDate && zDate[0] ){ | |
| 193 | - @ <td>%h(zDate) | |
| 194 | - }else{ | |
| 195 | - @ <td> | |
| 196 | - } | |
| 197 | - @ </tr> | |
| 198 | - } | |
| 199 | - db_finalize(&s); | |
| 160 | + if( zWith==0 || zWith[0]==0 ){ | |
| 161 | + style_submenu_element("Add", "setup_uedit"); | |
| 162 | + style_submenu_element("Log", "access_log"); | |
| 163 | + style_submenu_element("Help", "setup_ulist_notes"); | |
| 164 | + style_header("User List"); | |
| 165 | + @ <table border=1 cellpadding=2 cellspacing=0 class='userTable'> | |
| 166 | + @ <thead><tr> | |
| 167 | + @ <th>UID <th>Category | |
| 168 | + @ <th>Capabilities (<a href='%R/setup_ucap_list'>key</a>) | |
| 169 | + @ <th>Info <th>Last Change</tr></thead> | |
| 170 | + @ <tbody> | |
| 171 | + db_prepare(&s, | |
| 172 | + "SELECT uid, login, cap, date(mtime,'unixepoch')" | |
| 173 | + " FROM user" | |
| 174 | + " WHERE login IN ('anonymous','nobody','developer','reader')" | |
| 175 | + " ORDER BY login" | |
| 176 | + ); | |
| 177 | + while( db_step(&s)==SQLITE_ROW ){ | |
| 178 | + int uid = db_column_int(&s, 0); | |
| 179 | + const char *zLogin = db_column_text(&s, 1); | |
| 180 | + const char *zCap = db_column_text(&s, 2); | |
| 181 | + const char *zDate = db_column_text(&s, 4); | |
| 182 | + @ <tr> | |
| 183 | + @ <td><a href='setup_uedit?id=%d(uid)'>%d(uid)</a> | |
| 184 | + @ <td><a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a> | |
| 185 | + @ <td>%h(zCap) | |
| 186 | + | |
| 187 | + if( fossil_strcmp(zLogin,"anonymous")==0 ){ | |
| 188 | + @ <td>All logged-in users | |
| 189 | + }else if( fossil_strcmp(zLogin,"developer")==0 ){ | |
| 190 | + @ <td>Users with '<b>v</b>' capability | |
| 191 | + }else if( fossil_strcmp(zLogin,"nobody")==0 ){ | |
| 192 | + @ <td>All users without login | |
| 193 | + }else if( fossil_strcmp(zLogin,"reader")==0 ){ | |
| 194 | + @ <td>Users with '<b>u</b>' capability | |
| 195 | + }else{ | |
| 196 | + @ <td> | |
| 197 | + } | |
| 198 | + if( zDate && zDate[0] ){ | |
| 199 | + @ <td>%h(zDate) | |
| 200 | + }else{ | |
| 201 | + @ <td> | |
| 202 | + } | |
| 203 | + @ </tr> | |
| 204 | + } | |
| 205 | + db_finalize(&s); | |
| 206 | + }else{ | |
| 207 | + style_header("Users With Capabilities \"%h\"", zWith); | |
| 208 | + } | |
| 200 | 209 | @ </tbody></table> |
| 201 | 210 | @ <div class='section'>Users</div> |
| 202 | 211 | @ <table border=1 cellpadding=2 cellspacing=0 class='userTable' id='userlist'> |
| 203 | 212 | @ <thead><tr> |
| 204 | 213 | @ <th>ID<th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login</tr></thead> |
| @@ -213,20 +222,25 @@ | ||
| 213 | 222 | " SELECT uname, mtime FROM accesslog WHERE success" |
| 214 | 223 | " UNION ALL" |
| 215 | 224 | " SELECT login AS uname, rcvfrom.mtime AS mtime FROM rcvfrom JOIN user USING(uid))" |
| 216 | 225 | " GROUP BY 1;" |
| 217 | 226 | ); |
| 227 | + } | |
| 228 | + if( zWith && zWith[0] ){ | |
| 229 | + zWith = mprintf(" AND cap GLOB '*[%q]*'", zWith); | |
| 230 | + }else{ | |
| 231 | + zWith = ""; | |
| 218 | 232 | } |
| 219 | 233 | db_prepare(&s, |
| 220 | 234 | "SELECT uid, login, cap, info, date(mtime,'unixepoch'), lower(login) AS sortkey, " |
| 221 | 235 | " CASE WHEN info LIKE '%%expires 20%%'" |
| 222 | 236 | " THEN substr(info,instr(lower(info),'expires')+8,10)" |
| 223 | 237 | " END AS exp," |
| 224 | 238 | "atime" |
| 225 | 239 | " FROM user LEFT JOIN lastAccess ON login=uname" |
| 226 | - " WHERE login NOT IN ('anonymous','nobody','developer','reader')" | |
| 227 | - " ORDER BY sortkey" | |
| 240 | + " WHERE login NOT IN ('anonymous','nobody','developer','reader') %s" | |
| 241 | + " ORDER BY sortkey", zWith/*safe-for-%s*/ | |
| 228 | 242 | ); |
| 229 | 243 | rNow = db_double(0.0, "SELECT julianday('now');"); |
| 230 | 244 | while( db_step(&s)==SQLITE_ROW ){ |
| 231 | 245 | int uid = db_column_int(&s, 0); |
| 232 | 246 | const char *zLogin = db_column_text(&s, 1); |
| @@ -425,11 +439,12 @@ | ||
| 425 | 439 | zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid); |
| 426 | 440 | higherUser = zOldCaps && strchr(zOldCaps,'s'); |
| 427 | 441 | } |
| 428 | 442 | |
| 429 | 443 | if( P("can") ){ |
| 430 | - cgi_redirect("setup_ulist"); /* User pressed the Cancel button */ | |
| 444 | + /* User pressed the cancel button */ | |
| 445 | + cgi_redirect("setup_ulist"); | |
| 431 | 446 | return; |
| 432 | 447 | } |
| 433 | 448 | |
| 434 | 449 | /* If we have all the necessary information, write the new or |
| 435 | 450 | ** modified user record. After writing the user record, redirect |
| 436 | 451 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -139,66 +139,75 @@ | |
| 139 | /* |
| 140 | ** WEBPAGE: setup_ulist |
| 141 | ** |
| 142 | ** Show a list of users. Clicking on any user jumps to the edit |
| 143 | ** screen for that user. Requires Admin privileges. |
| 144 | */ |
| 145 | void setup_ulist(void){ |
| 146 | Stmt s; |
| 147 | double rNow; |
| 148 | |
| 149 | login_check_credentials(); |
| 150 | if( !g.perm.Admin ){ |
| 151 | login_needed(0); |
| 152 | return; |
| 153 | } |
| 154 | |
| 155 | style_submenu_element("Add", "setup_uedit"); |
| 156 | style_submenu_element("Log", "access_log"); |
| 157 | style_submenu_element("Help", "setup_ulist_notes"); |
| 158 | style_header("User List"); |
| 159 | @ <table border=1 cellpadding=2 cellspacing=0 class='userTable'> |
| 160 | @ <thead><tr> |
| 161 | @ <th>UID <th>Category |
| 162 | @ <th>Capabilities (<a href='%R/setup_ucap_list'>key</a>) |
| 163 | @ <th>Info <th>Last Change</tr></thead> |
| 164 | @ <tbody> |
| 165 | db_prepare(&s, |
| 166 | "SELECT uid, login, cap, date(mtime,'unixepoch')" |
| 167 | " FROM user" |
| 168 | " WHERE login IN ('anonymous','nobody','developer','reader')" |
| 169 | " ORDER BY login" |
| 170 | ); |
| 171 | while( db_step(&s)==SQLITE_ROW ){ |
| 172 | int uid = db_column_int(&s, 0); |
| 173 | const char *zLogin = db_column_text(&s, 1); |
| 174 | const char *zCap = db_column_text(&s, 2); |
| 175 | const char *zDate = db_column_text(&s, 4); |
| 176 | @ <tr> |
| 177 | @ <td><a href='setup_uedit?id=%d(uid)'>%d(uid)</a> |
| 178 | @ <td><a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a> |
| 179 | @ <td>%h(zCap) |
| 180 | |
| 181 | if( fossil_strcmp(zLogin,"anonymous")==0 ){ |
| 182 | @ <td>All logged-in users |
| 183 | }else if( fossil_strcmp(zLogin,"developer")==0 ){ |
| 184 | @ <td>Users with '<b>v</b>' capability |
| 185 | }else if( fossil_strcmp(zLogin,"nobody")==0 ){ |
| 186 | @ <td>All users without login |
| 187 | }else if( fossil_strcmp(zLogin,"reader")==0 ){ |
| 188 | @ <td>Users with '<b>u</b>' capability |
| 189 | }else{ |
| 190 | @ <td> |
| 191 | } |
| 192 | if( zDate && zDate[0] ){ |
| 193 | @ <td>%h(zDate) |
| 194 | }else{ |
| 195 | @ <td> |
| 196 | } |
| 197 | @ </tr> |
| 198 | } |
| 199 | db_finalize(&s); |
| 200 | @ </tbody></table> |
| 201 | @ <div class='section'>Users</div> |
| 202 | @ <table border=1 cellpadding=2 cellspacing=0 class='userTable' id='userlist'> |
| 203 | @ <thead><tr> |
| 204 | @ <th>ID<th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login</tr></thead> |
| @@ -213,20 +222,25 @@ | |
| 213 | " SELECT uname, mtime FROM accesslog WHERE success" |
| 214 | " UNION ALL" |
| 215 | " SELECT login AS uname, rcvfrom.mtime AS mtime FROM rcvfrom JOIN user USING(uid))" |
| 216 | " GROUP BY 1;" |
| 217 | ); |
| 218 | } |
| 219 | db_prepare(&s, |
| 220 | "SELECT uid, login, cap, info, date(mtime,'unixepoch'), lower(login) AS sortkey, " |
| 221 | " CASE WHEN info LIKE '%%expires 20%%'" |
| 222 | " THEN substr(info,instr(lower(info),'expires')+8,10)" |
| 223 | " END AS exp," |
| 224 | "atime" |
| 225 | " FROM user LEFT JOIN lastAccess ON login=uname" |
| 226 | " WHERE login NOT IN ('anonymous','nobody','developer','reader')" |
| 227 | " ORDER BY sortkey" |
| 228 | ); |
| 229 | rNow = db_double(0.0, "SELECT julianday('now');"); |
| 230 | while( db_step(&s)==SQLITE_ROW ){ |
| 231 | int uid = db_column_int(&s, 0); |
| 232 | const char *zLogin = db_column_text(&s, 1); |
| @@ -425,11 +439,12 @@ | |
| 425 | zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid); |
| 426 | higherUser = zOldCaps && strchr(zOldCaps,'s'); |
| 427 | } |
| 428 | |
| 429 | if( P("can") ){ |
| 430 | cgi_redirect("setup_ulist"); /* User pressed the Cancel button */ |
| 431 | return; |
| 432 | } |
| 433 | |
| 434 | /* If we have all the necessary information, write the new or |
| 435 | ** modified user record. After writing the user record, redirect |
| 436 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -139,66 +139,75 @@ | |
| 139 | /* |
| 140 | ** WEBPAGE: setup_ulist |
| 141 | ** |
| 142 | ** Show a list of users. Clicking on any user jumps to the edit |
| 143 | ** screen for that user. Requires Admin privileges. |
| 144 | ** |
| 145 | ** Query parameters: |
| 146 | ** |
| 147 | ** with=CAP Only show users that have one or more capabilities in CAP. |
| 148 | */ |
| 149 | void setup_ulist(void){ |
| 150 | Stmt s; |
| 151 | double rNow; |
| 152 | const char *zWith = P("with"); |
| 153 | |
| 154 | login_check_credentials(); |
| 155 | if( !g.perm.Admin ){ |
| 156 | login_needed(0); |
| 157 | return; |
| 158 | } |
| 159 | |
| 160 | if( zWith==0 || zWith[0]==0 ){ |
| 161 | style_submenu_element("Add", "setup_uedit"); |
| 162 | style_submenu_element("Log", "access_log"); |
| 163 | style_submenu_element("Help", "setup_ulist_notes"); |
| 164 | style_header("User List"); |
| 165 | @ <table border=1 cellpadding=2 cellspacing=0 class='userTable'> |
| 166 | @ <thead><tr> |
| 167 | @ <th>UID <th>Category |
| 168 | @ <th>Capabilities (<a href='%R/setup_ucap_list'>key</a>) |
| 169 | @ <th>Info <th>Last Change</tr></thead> |
| 170 | @ <tbody> |
| 171 | db_prepare(&s, |
| 172 | "SELECT uid, login, cap, date(mtime,'unixepoch')" |
| 173 | " FROM user" |
| 174 | " WHERE login IN ('anonymous','nobody','developer','reader')" |
| 175 | " ORDER BY login" |
| 176 | ); |
| 177 | while( db_step(&s)==SQLITE_ROW ){ |
| 178 | int uid = db_column_int(&s, 0); |
| 179 | const char *zLogin = db_column_text(&s, 1); |
| 180 | const char *zCap = db_column_text(&s, 2); |
| 181 | const char *zDate = db_column_text(&s, 4); |
| 182 | @ <tr> |
| 183 | @ <td><a href='setup_uedit?id=%d(uid)'>%d(uid)</a> |
| 184 | @ <td><a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a> |
| 185 | @ <td>%h(zCap) |
| 186 | |
| 187 | if( fossil_strcmp(zLogin,"anonymous")==0 ){ |
| 188 | @ <td>All logged-in users |
| 189 | }else if( fossil_strcmp(zLogin,"developer")==0 ){ |
| 190 | @ <td>Users with '<b>v</b>' capability |
| 191 | }else if( fossil_strcmp(zLogin,"nobody")==0 ){ |
| 192 | @ <td>All users without login |
| 193 | }else if( fossil_strcmp(zLogin,"reader")==0 ){ |
| 194 | @ <td>Users with '<b>u</b>' capability |
| 195 | }else{ |
| 196 | @ <td> |
| 197 | } |
| 198 | if( zDate && zDate[0] ){ |
| 199 | @ <td>%h(zDate) |
| 200 | }else{ |
| 201 | @ <td> |
| 202 | } |
| 203 | @ </tr> |
| 204 | } |
| 205 | db_finalize(&s); |
| 206 | }else{ |
| 207 | style_header("Users With Capabilities \"%h\"", zWith); |
| 208 | } |
| 209 | @ </tbody></table> |
| 210 | @ <div class='section'>Users</div> |
| 211 | @ <table border=1 cellpadding=2 cellspacing=0 class='userTable' id='userlist'> |
| 212 | @ <thead><tr> |
| 213 | @ <th>ID<th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login</tr></thead> |
| @@ -213,20 +222,25 @@ | |
| 222 | " SELECT uname, mtime FROM accesslog WHERE success" |
| 223 | " UNION ALL" |
| 224 | " SELECT login AS uname, rcvfrom.mtime AS mtime FROM rcvfrom JOIN user USING(uid))" |
| 225 | " GROUP BY 1;" |
| 226 | ); |
| 227 | } |
| 228 | if( zWith && zWith[0] ){ |
| 229 | zWith = mprintf(" AND cap GLOB '*[%q]*'", zWith); |
| 230 | }else{ |
| 231 | zWith = ""; |
| 232 | } |
| 233 | db_prepare(&s, |
| 234 | "SELECT uid, login, cap, info, date(mtime,'unixepoch'), lower(login) AS sortkey, " |
| 235 | " CASE WHEN info LIKE '%%expires 20%%'" |
| 236 | " THEN substr(info,instr(lower(info),'expires')+8,10)" |
| 237 | " END AS exp," |
| 238 | "atime" |
| 239 | " FROM user LEFT JOIN lastAccess ON login=uname" |
| 240 | " WHERE login NOT IN ('anonymous','nobody','developer','reader') %s" |
| 241 | " ORDER BY sortkey", zWith/*safe-for-%s*/ |
| 242 | ); |
| 243 | rNow = db_double(0.0, "SELECT julianday('now');"); |
| 244 | while( db_step(&s)==SQLITE_ROW ){ |
| 245 | int uid = db_column_int(&s, 0); |
| 246 | const char *zLogin = db_column_text(&s, 1); |
| @@ -425,11 +439,12 @@ | |
| 439 | zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid); |
| 440 | higherUser = zOldCaps && strchr(zOldCaps,'s'); |
| 441 | } |
| 442 | |
| 443 | if( P("can") ){ |
| 444 | /* User pressed the cancel button */ |
| 445 | cgi_redirect("setup_ulist"); |
| 446 | return; |
| 447 | } |
| 448 | |
| 449 | /* If we have all the necessary information, write the new or |
| 450 | ** modified user record. After writing the user record, redirect |
| 451 |