| | @@ -109,10 +109,12 @@ |
| 109 | 109 | "Show artifacts that are shunned by this repository"); |
| 110 | 110 | setup_menu_entry("Log", "rcvfromlist", |
| 111 | 111 | "A record of received artifacts and their sources"); |
| 112 | 112 | setup_menu_entry("User-Log", "access_log", |
| 113 | 113 | "A record of login attempts"); |
| 114 | + setup_menu_entry("Admin-Log", "admin_log", |
| 115 | + "View the admin_log entries"); |
| 114 | 116 | setup_menu_entry("Stats", "stat", |
| 115 | 117 | "Display repository statistics"); |
| 116 | 118 | setup_menu_entry("SQL", "admin_sql", |
| 117 | 119 | "Enter raw SQL commands"); |
| 118 | 120 | setup_menu_entry("TH1", "admin_th1", |
| | @@ -380,12 +382,14 @@ |
| 380 | 382 | } |
| 381 | 383 | login_verify_csrf_secret(); |
| 382 | 384 | db_multi_exec( |
| 383 | 385 | "REPLACE INTO user(uid,login,info,pw,cap,mtime) " |
| 384 | 386 | "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())", |
| 385 | | - uid, P("login"), P("info"), zPw, zCap |
| 387 | + uid, zLogin, P("info"), zPw, zCap |
| 386 | 388 | ); |
| 389 | + admin_log( "Updated user [%q] with capabilities [%q].", |
| 390 | + zLogin, zCap ); |
| 387 | 391 | if( atoi(PD("all","0"))>0 ){ |
| 388 | 392 | Blob sql; |
| 389 | 393 | char *zErr = 0; |
| 390 | 394 | blob_zero(&sql); |
| 391 | 395 | if( zOldLogin==0 ){ |
| | @@ -407,12 +411,16 @@ |
| 407 | 411 | zLogin, P("pw"), zLogin, P("info"), zCap, |
| 408 | 412 | zOldLogin |
| 409 | 413 | ); |
| 410 | 414 | login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr); |
| 411 | 415 | blob_reset(&sql); |
| 416 | + admin_log( "Updated user [%q] in all login groups " |
| 417 | + "with capabilities [%q].", |
| 418 | + zLogin, zCap ); |
| 412 | 419 | if( zErr ){ |
| 413 | 420 | style_header("User Change Error"); |
| 421 | + admin_log( "Error updating user '%q': %s'.", zLogin, zErr ); |
| 414 | 422 | @ <span class="loginError">%s(zErr)</span> |
| 415 | 423 | @ |
| 416 | 424 | @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p> |
| 417 | 425 | style_footer(); |
| 418 | 426 | return; |
| | @@ -862,10 +870,12 @@ |
| 862 | 870 | if( zQ ){ |
| 863 | 871 | int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ); |
| 864 | 872 | if( iQ!=iVal ){ |
| 865 | 873 | login_verify_csrf_secret(); |
| 866 | 874 | db_set(zVar, iQ ? "1" : "0", 0); |
| 875 | + admin_log("Set option [%q] to [%q].", |
| 876 | + zVar, iQ ? "on" : "off"); |
| 867 | 877 | iVal = iQ; |
| 868 | 878 | } |
| 869 | 879 | } |
| 870 | 880 | @ <input type="checkbox" name="%s(zQParm)" |
| 871 | 881 | if( iVal ){ |
| | @@ -889,12 +899,15 @@ |
| 889 | 899 | int disabled /* 1 if disabled */ |
| 890 | 900 | ){ |
| 891 | 901 | const char *zVal = db_get(zVar, zDflt); |
| 892 | 902 | const char *zQ = P(zQParm); |
| 893 | 903 | if( zQ && fossil_strcmp(zQ,zVal)!=0 ){ |
| 904 | + const int nZQ = (int)strlen(zQ); |
| 894 | 905 | login_verify_csrf_secret(); |
| 895 | 906 | db_set(zVar, zQ, 0); |
| 907 | + admin_log("Set entry_attribute %Q to: %.*s%s", |
| 908 | + zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 896 | 909 | zVal = zQ; |
| 897 | 910 | } |
| 898 | 911 | @ <input type="text" id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" |
| 899 | 912 | if( disabled ){ |
| 900 | 913 | @ disabled="disabled" |
| | @@ -915,12 +928,15 @@ |
| 915 | 928 | int disabled /* 1 if the textarea should not be editable */ |
| 916 | 929 | ){ |
| 917 | 930 | const char *z = db_get(zVar, (char*)zDflt); |
| 918 | 931 | const char *zQ = P(zQP); |
| 919 | 932 | if( zQ && !disabled && fossil_strcmp(zQ,z)!=0){ |
| 933 | + const int nZQ = (int)strlen(zQ); |
| 920 | 934 | login_verify_csrf_secret(); |
| 921 | 935 | db_set(zVar, zQ, 0); |
| 936 | + admin_log("Set textarea_attribute %Q to: %.*s%s", |
| 937 | + zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 922 | 938 | z = zQ; |
| 923 | 939 | } |
| 924 | 940 | if( rows>0 && cols>0 ){ |
| 925 | 941 | @ <textarea id="id%s(zQP)" name="%s(zQP)" rows="%d(rows)" |
| 926 | 942 | if( disabled ){ |
| | @@ -946,12 +962,15 @@ |
| 946 | 962 | ){ |
| 947 | 963 | const char *z = db_get(zVar, (char*)zDflt); |
| 948 | 964 | const char *zQ = P(zQP); |
| 949 | 965 | int i; |
| 950 | 966 | if( zQ && fossil_strcmp(zQ,z)!=0){ |
| 967 | + const int nZQ = (int)strlen(zQ); |
| 951 | 968 | login_verify_csrf_secret(); |
| 952 | 969 | db_set(zVar, zQ, 0); |
| 970 | + admin_log("Set multiple_choice_attribute %Q to: %.*s%s", |
| 971 | + zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 953 | 972 | z = zQ; |
| 954 | 973 | } |
| 955 | 974 | @ <select size="1" name="%s(zQP)" id="id%s(zQP)"> |
| 956 | 975 | for(i=0; i<nChoice*2; i+=2){ |
| 957 | 976 | const char *zSel = fossil_strcmp(azChoice[i],z)==0 ? " selected" : ""; |
| | @@ -2023,5 +2042,83 @@ |
| 2023 | 2042 | @ <pre class="th1error">%h(zR)</pre> |
| 2024 | 2043 | } |
| 2025 | 2044 | } |
| 2026 | 2045 | style_footer(); |
| 2027 | 2046 | } |
| 2047 | + |
| 2048 | +static void admin_log_render_limits(){ |
| 2049 | + int const count = db_int(0,"SELECT COUNT(*) FROM admin_log"); |
| 2050 | + int i; |
| 2051 | + int limits[] = { |
| 2052 | + 10, 20, 50, 100, 250, 500, 0 |
| 2053 | + }; |
| 2054 | + for(i = 0; limits[i]; ++i ){ |
| 2055 | + cgi_printf("%s<a href='?n=%d'>%d</a>", |
| 2056 | + i ? " " : "", |
| 2057 | + limits[i], limits[i]); |
| 2058 | + if(limits[i]>count) break; |
| 2059 | + } |
| 2060 | +} |
| 2061 | + |
| 2062 | +/* |
| 2063 | +** WEBPAGE: admin_log |
| 2064 | +** |
| 2065 | +** Shows the contents of the admin_log table, which is only created if |
| 2066 | +** the admin-log setting is enabled. Requires Admin or Setup ('a' or |
| 2067 | +** 's') permissions. |
| 2068 | +*/ |
| 2069 | +void page_admin_log(){ |
| 2070 | + Stmt stLog = empty_Stmt; |
| 2071 | + Blob qLog = empty_blob; |
| 2072 | + int limit; |
| 2073 | + int fLogEnabled; |
| 2074 | + int counter = 0; |
| 2075 | + login_check_credentials(); |
| 2076 | + if( !g.perm.Setup && !g.perm.Admin ){ |
| 2077 | + login_needed(); |
| 2078 | + } |
| 2079 | + style_header("Admin Log"); |
| 2080 | + create_admin_log_table(); |
| 2081 | + limit = atoi(PD("n","20")); |
| 2082 | + fLogEnabled = db_get_boolean("admin-log", 0); |
| 2083 | + @ <div>Admin logging is %s(fLogEnabled?"on":"off").</div> |
| 2084 | + |
| 2085 | + |
| 2086 | + @ <div>Limit results to: <span> |
| 2087 | + admin_log_render_limits(); |
| 2088 | + @ </span></div> |
| 2089 | + |
| 2090 | + blob_append_sql(&qLog, |
| 2091 | + "SELECT datetime(time,'unixepoch'), who, page, what " |
| 2092 | + "FROM admin_log " |
| 2093 | + "ORDER BY time DESC "); |
| 2094 | + if(limit>0){ |
| 2095 | + @ %d(limit) Most recent entries: |
| 2096 | + blob_append_sql(&qLog, "LIMIT %d", limit); |
| 2097 | + } |
| 2098 | + db_prepare(&stLog, "%s", blob_sql_text(&qLog)); |
| 2099 | + blob_reset(&qLog); |
| 2100 | + @ <table id="adminLogTable" class="adminLogTable" width="100%%"> |
| 2101 | + @ <thead> |
| 2102 | + @ <th>Time</th> |
| 2103 | + @ <th>User</th> |
| 2104 | + @ <th>Page</th> |
| 2105 | + @ <th width="60%%">Message</th> |
| 2106 | + @ </thead><tbody> |
| 2107 | + while( SQLITE_ROW == db_step(&stLog) ){ |
| 2108 | + char const * zTime = db_column_text(&stLog, 0); |
| 2109 | + char const * zUser = db_column_text(&stLog, 1); |
| 2110 | + char const * zPage = db_column_text(&stLog, 2); |
| 2111 | + char const * zMessage = db_column_text(&stLog, 3); |
| 2112 | + @ <tr class="row%d(counter++%2)"> |
| 2113 | + @ <td class="adminTime">%s(zTime)</td> |
| 2114 | + @ <td>%s(zUser)</td> |
| 2115 | + @ <td>%s(zPage)</td> |
| 2116 | + @ <td>%h(zMessage)</td> |
| 2117 | + @ </tr> |
| 2118 | + } |
| 2119 | + @ </tbody></table> |
| 2120 | + if(limit>0 && counter<limit){ |
| 2121 | + @ <div>%d(counter) entries shown.</div> |
| 2122 | + } |
| 2123 | + style_footer(); |
| 2124 | +} |
| 2028 | 2125 | |