/* ** Copyright (c) 2018 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used managing user capability strings. */ #include "config.h" #include "capabilities.h" #include #if INTERFACE /* ** A capability string object holds all defined capabilities in a ** vector format that is subject to boolean operations. */ struct CapabilityString { unsigned char x[128]; }; #endif /* ** Add capabilities to a CapabilityString. If pIn is NULL, then create ** a new capability string. ** ** Call capability_free() on the allocated CapabilityString object to ** deallocate. */ CapabilityString *capability_add(CapabilityString *pIn, const char *zCap){ int c; int i; if( pIn==0 ){ pIn = fossil_malloc( sizeof(*pIn) ); memset(pIn, 0, sizeof(*pIn)); } if( zCap ){ for(i=0; (c = zCap[i])!=0; i++){ if( c>='0' && c<='z' ) pIn->x[c] = 1; } } return pIn; } /* ** Remove capabilities from a CapabilityString. */ CapabilityString *capability_remove(CapabilityString *pIn, const char *zCap){ int c; int i; if( pIn==0 ){ pIn = fossil_malloc( sizeof(*pIn) ); memset(pIn, 0, sizeof(*pIn)); } if( zCap ){ for(i=0; (c = zCap[i])!=0; i++){ if( c>='0' && c<='z' ) pIn->x[c] = 0; } } return pIn; } /* ** Delete a CapabilityString object. */ void capability_free(CapabilityString *p){ fossil_free(p); } /* ** Expand the capability string by including all capabilities for ** special users "nobody" and "anonymous". Also include "reader" ** if "u" is present and "developer" if "v" is present. */ void capability_expand(CapabilityString *pIn){ static char *zNobody = 0; static char *zAnon = 0; static char *zReader = 0; static char *zDev = 0; if( pIn==0 ){ fossil_free(zNobody); zNobody = 0; fossil_free(zAnon); zAnon = 0; fossil_free(zReader); zReader = 0; fossil_free(zDev); zDev = 0; return; } if( pIn->x['v'] ){ if( zDev==0 ){ zDev = db_text(0, "SELECT cap FROM user WHERE login='developer'"); } pIn = capability_add(pIn, zDev); } if( pIn->x['u'] ){ if( zReader==0 ){ zReader = db_text(0, "SELECT cap FROM user WHERE login='reader'"); } pIn = capability_add(pIn, zReader); } if( zNobody==0 ){ zNobody = db_text(0, "SELECT cap FROM user WHERE login='nobody'"); zAnon = db_text(0, "SELECT cap FROM user WHERE login='anonymous'"); } pIn = capability_add(pIn, zAnon); pIn = capability_add(pIn, zNobody); } /* ** Render a capability string in canonical string format. Space to hold ** the returned string is obtained from fossil_malloc() can should be freed ** by the caller. */ char *capability_string(CapabilityString *p){ Blob out; int i; int j = 0; char buf[100]; blob_init(&out, 0, 0); for(i='a'; i<='z'; i++){ if( p->x[i] ) buf[j++] = i; } for(i='0'; i<='9'; i++){ if( p->x[i] ) buf[j++] = i; } for(i='A'; i<='Z'; i++){ if( p->x[i] ) buf[j++] = i; } buf[j] = 0; return fossil_strdup(buf); } /* ** The next two routines implement an aggregate SQL function that ** takes multiple capability strings and in the end returns their ** union. Example usage: ** ** SELECT capunion(cap) FROM user WHERE login IN ('nobody','anonymous'); */ void capability_union_step( sqlite3_context *context, int argc, sqlite3_value **argv ){ CapabilityString *p; const char *zIn; zIn = (const char*)sqlite3_value_text(argv[0]); if( zIn==0 ) return; p = (CapabilityString*)sqlite3_aggregate_context(context, sizeof(*p)); p = capability_add(p, zIn); } void capability_union_finalize(sqlite3_context *context){ CapabilityString *p; p = sqlite3_aggregate_context(context, 0); if( p ){ char *zOut = capability_string(p); sqlite3_result_text(context, zOut, -1, fossil_free); } } /* ** The next routines takes the raw USER.CAP field and expands it with ** capabilities from special users. Example: ** ** SELECT fullcap(cap) FROM user WHERE login=?1 */ void capability_fullcap( sqlite3_context *context, int argc, sqlite3_value **argv ){ CapabilityString *p; const char *zIn; char *zOut; zIn = (const char*)sqlite3_value_text(argv[0]); if( zIn==0 ) zIn = ""; p = capability_add(0, zIn); capability_expand(p); zOut = capability_string(p); sqlite3_result_text(context, zOut, -1, fossil_free); capability_free(p); } /* ** Generate HTML that lists all of the capability letters together with ** a brief summary of what each letter means. */ void capabilities_table(void){ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @
aAdmin: Create and delete users
bAttach: Add attachments to wiki or tickets
cAppend-Tkt: Append to tickets
dDelete: Delete wiki and tickets
eView-PII: \ @ View sensitive data such as email addresses
fNew-Wiki: Create new wiki pages
gClone: Clone the repository
hHyperlinks: Show hyperlinks to detailed @ repository history
iCheck-In: Commit new versions in the repository
jRead-Wiki: View wiki pages
kWrite-Wiki: Edit wiki pages
lMod-Wiki: Moderator for wiki pages
mAppend-Wiki: Append to wiki pages
nNew-Tkt: Create new tickets
oCheck-Out: Check out versions
pPassword: Change your own password
qMod-Tkt: Moderator for tickets
rRead-Tkt: View tickets
sSetup/Super-user: Setup and configure this website
tTkt-Report: Create new bug summary reports
uReader: Inherit privileges of @ user reader
vDeveloper: Inherit privileges of @ user developer
wWrite-Tkt: Edit tickets
xPrivate: Push and/or pull private branches
yWrite-Unver: Push unversioned files
zZip download: Download a ZIP archive or tarball
2Forum-Read: Read forum posts by others
3Forum-Append: Add new forum posts
4Forum-Trusted: Add pre-approved forum posts
5Forum-Moderator: Approve or disapprove forum posts
6Forum-Supervisor: \ @ Forum administrator: Set or remove capability "4" for other users @
7Email-Alerts: Sign up for email nofications
AAnnounce: Send announcements
DDebug: Enable debugging features
} /* ** Generate a "capability summary table" that shows the major capabilities ** against the various user categories. */ void capability_summary(void){ Stmt q; db_prepare(&q, "WITH t(id,seq) AS (VALUES('nobody',1),('anonymous',2),('reader',3)," "('developer',4))" " SELECT id, fullcap(user.cap),seq,1" " FROM t LEFT JOIN user ON t.id=user.login" " UNION ALL" " SELECT 'New User Default', fullcap(%Q), 10, 1" " UNION ALL" " SELECT 'Regular User', fullcap(capunion(cap)), 20, count(*) FROM user" " WHERE cap NOT GLOB '*[as]*'" " UNION ALL" " SELECT 'Adminstator', fullcap(capunion(cap)), 30, count(*) FROM user" " WHERE cap GLOB '*[as]*'" " ORDER BY 3 ASC", db_get("default-perms","") ); @ @ while( db_step(&q)==SQLITE_ROW ){ const char *zId = db_column_text(&q, 0); const char *zCap = db_column_text(&q, 1); int n = db_column_int(&q, 3); int eType; static const char *azType[] = { "off", "read", "write" }; static const char *azClass[] = { "capsumOff", "capsumRead", "capsumWrite" }; if( n==0 ) continue; /* Code */ if( db_column_int(&q,2)<10 ){ @ }else if( n>1 ){ @ }else{ @ } if( sqlite3_strglob("*[asi]*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*[oz]*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ /* Forum */ if( sqlite3_strglob("*[as3456]*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*2*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ /* Ticket */ if( sqlite3_strglob("*[ascdnqtw]*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*r*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ /* Wiki */ if( sqlite3_strglob("*[asdfjlm]*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*j*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ /* Unversioned */ if( sqlite3_strglob("*y*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*[ioas]*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ } db_finalize(&q); @
 CodeForumTicketsWiki\ @ Unversioned Content
"%h(zId)"
%d(n) %h(zId)s
%h(zId)%s(azType[eType])%s(azType[eType])%s(azType[eType])%s(azType[eType])%s(azType[eType])
}