下面公布一个目前在我们公司使用的apache module的源代码。
我们公司开发人员很多,使用了SVN和viewvc来进行版本控制和查看,通过web界面,SVN能够根据每个用户的权限来控制能够浏览某个项目下的代码,但是viewvc只要你在SVN中有用户,你就可以看当前SVN中所有项目的代码。这个风险比较大,因此,我们开发了一个apache module,用来读取SVN的权限配置文件,把相应的权限集成到VIEWVC中。
源代码:
#include “apr_strings.h” #include “apr_hash.h” #include “apr_tables.h” #include “apr_md5.h” #include “apr_lib.h” #include “apr_base64.h” #define APR_WANT_STRFUNC #include “apr_want.h” #include “ap_config.h” #include “httpd.h” #include “http_config.h” #include “http_core.h” #include “http_log.h” #include “http_protocol.h” #include “http_request.h” #include “ap_provider.h” #include #define ENABLED 1 #define DISABLED 0 typedef struct { short enabled; short debug; char *dir; // the starting path char *prefixPath; // the stop url pattern char *stopPattern; // the svn access file char *accessFile; } authSVN_rec; struct access_rec { // 0: group // 1: user // 2: all short type; // the group or user name char *name; // 0: don’t have read access // 1: have read access short access; // the next access record struct access_rec *next; }; module AP_MODULE_DECLARE_DATA authSVN_module; // src starts with start static short start_with(const char *src, const char *start) { int i = 0; if(strlen(src) < strlen(start)) return 0; i = strlen(start) - 1; while(i >= 0) { if(src[i] != start[i]) return 0; i–; } return 1; } // parse the SVN access file static short parse_access_file(request_rec *r, const char* file, const authSVN_rec *conf, apr_hash_t* ugMap, apr_hash_t* accessMap) { ap_configfile_t *f = NULL; apr_status_t status; char l[MAX_STRING_LEN], dir[256]; status = ap_pcfg_openfile(&f, r->pool, file); short flag = 0; if (status != APR_SUCCESS) return 0; while(!(ap_cfg_getline(l, MAX_STRING_LEN, f))) { const char *w = NULL; char *last = NULL; apr_table_t *apt = NULL; struct access_rec *arec = NULL, *arecp = NULL; if ((l[0] == ‘#’) || (!l[0])) { continue; } if(start_with(l, “[groups]”)) { flag = 1; continue; } if(l[0] == ‘[’) { flag = 2; w = apr_strtok(l, “[]:\n”, &last); if(w && w[0] == ‘/’) { // the root directory snprintf(dir, sizeof(dir), “%s”, conf->prefixPath); dir[strlen(dir) - 1] = ‘\0′; } else if(w && w[0] != ‘/’) { const char *project = w; w = apr_strtok(NULL, “[]:\n”, &last); if(w) { snprintf(dir, sizeof(dir), “%s%s%s”, conf->prefixPath, project, w); // make sure the dir is not end with / int len = strlen(dir); if(dir[len - 1] == ‘/’) dir[len - 1] = ‘\0′; } else dir[0] = ‘\0′; } else { dir[0] = ‘\0′; } continue; } if(flag == 1) { // this is the groups and users definition w = apr_strtok(l, “=, \n”, &last); if(w == NULL) // group name not found continue; apt = (apr_table_t *)apr_hash_get(ugMap, (const void *)w, APR_HASH_KEY_STRING); if(apt == NULL) { apt = apr_table_make(r->pool, 10); apr_hash_set(ugMap, (const void *)apr_pstrdup(r->pool, w), APR_HASH_KEY_STRING, (const void *)apt); } while((w = apr_strtok(NULL, “=, \n”, &last)) != NULL) { // this is group name or user name if(w[0] == ‘@’) { w++; if(w) { apr_table_setn(apt, apr_pstrdup(r->pool, w), “0″); } } else { // this is user name apr_table_setn(apt, apr_pstrdup(r->pool, w), “1″); } } } if(flag == 2) { if(dir[0] == ‘\0′) continue; w = apr_strtok(l, “= \n”, &last); if(w) { arec = (struct access_rec *)apr_pcalloc(r->pool, sizeof(struct access_rec)); arec->access = 0; if(w[0] == ‘@’) { w++; if(w) { arec->name = apr_pstrdup(r->pool, w); arec->type = 0; } else continue; } else if(w[0] == ‘*’) { arec->name = apr_pstrdup(r->pool, “*”); // this is all arec->type = 2; } else { arec->name = apr_pstrdup(r->pool, w); // this is user name arec->type = 1; } } else continue; w = apr_strtok(NULL, “= \n”, &last); if(!w) { arec->access = 0; } else { arec->access = 1; } arecp = (struct access_rec *)apr_hash_get(accessMap, (const void *)dir, APR_HASH_KEY_STRING); if(arecp == NULL) { arec->next = NULL; apr_hash_set(accessMap, (const void *)apr_pstrdup(r->pool, dir), APR_HASH_KEY_STRING, (const void *)arec); } else { while(arecp->next != NULL) arecp = arecp->next; arecp->next = arec; } } } ap_cfg_closefile(f); return 1; } static void *create_authSVN_dir_config(apr_pool_t *p, char *d) { authSVN_rec *conf = (authSVN_rec *)apr_pcalloc(p, sizeof(*conf)); if(conf == NULL) return NULL; conf->enabled = DISABLED; conf->debug = DISABLED; conf->dir = d; conf->prefixPath = NULL; conf->stopPattern = NULL; conf->accessFile = NULL; return conf; } static char hex2int(char c) { if( c>=’0′ && c<='9' ) return (c - '0'); return (c - 'A' + 10); } static void url_decode(char *url) { char *p = url; char *p1 = NULL; while(*p) { if(*p == '+') *p = ' '; if(*p=='%' && *(p+1) && *(p+2)) { *p = hex2int(toupper(*(p+1))) * 16 + hex2int(toupper(*(p+ 2))); strcpy(p + 1, p + 3); } if(*p=='\\' && *(p+1) && *(p+2) && *(p+3)) { p1 = p + 1; if(*p1 && *p1=='x') { *p = hex2int(toupper(*(p+2))) * 16 + hex2int(toupper(*(p+3))); strcpy(p+1, p+4); } } p++; } return; }
接上段
static void parent_path(char *url) { char *p = url + strlen(url) - 1; while(p != url && *p != '/') { *p = '\0'; p--; } if(p != url && *p=='/') *p = '\0'; return; } // return // 0: the user don't belong to this group // 1: the user belong to this group static short find_user_in_group(const char* user, const char *group, apr_hash_t* ugMap) { apr_table_t *apt = (apr_table_t *)apr_hash_get(ugMap, (const void *)group, APR_HASH_KEY_STRING); if(apt == NULL) return 0; apr_array_header_t *arr; apr_table_entry_t *elts; int i; arr = (apr_array_header_t *)apr_table_elts(apt); elts = (apr_table_entry_t *)arr->elts; for(i=0; inelts; i++) { if(elts[i].key == NULL || elts[i].val == NULL) continue; if(elts[i].val[0] == ‘1′ && strcmp(elts[i].key, user) == 0) { return 1; } if(elts[i].val[0] == ‘0′) { if(find_user_in_group(user, elts[i].key, ugMap)) return 1; } } return 0; } // return // 0:don’t have access // 1:have read access // 2:access not found static short find_access(const char* user, const char* url, apr_hash_t* ugMap, apr_hash_t* accessMap) { struct access_rec *arec= (struct access_rec *)apr_hash_get(accessMap, (const void *)url, APR_HASH_KEY_STRING); short access = 2; while(arec != NULL) { if(strcmp(arec->name, “*”) == 0) { // specified access to all users and groups on this url access = arec->access; } if(arec->type == 1 && strcmp(arec->name, user) == 0) { // specified user access on this url access = arec->access; } if(arec->type == 0) { // this is group access if(find_user_in_group(user, arec->name, ugMap)) access = arec->access; } // if this user have access, we return if(access == 1) return access; arec = arec->next; } return access; } static short estimate_access( request_rec *r, const authSVN_rec* conf, char* url, apr_hash_t* ugMap, apr_hash_t* accessMap ) { const char* user = r->user; // unauthorized if(!user || !user[0]) return 0; short access = find_access(user, url, ugMap, accessMap); if(access < 2) return access; if(url[0] == '/' && url[1] == '\0') return 0; parent_path(url); return estimate_access(r, conf, url, ugMap, accessMap); } // do regexp matching static short regexp_match(char *str, char *pattern) { regex_t reg; regmatch_t pm[1]; const size_t nmatch = 1; int res = 0; short r = 0; char ebuf[MAX_STRING_LEN]; res = regcomp(®, pattern, REG_EXTENDED); if(res != 0) { regfree(®); return 0; } res = regexec(®, str, nmatch, pm, 0); if(res == REG_NOMATCH) r = 0; else r = 1; regfree(®); return r; } static int authSVN_handler(request_rec *r) { authSVN_rec *conf = ap_get_module_config(r->per_dir_config, &authSVN_module); if(!conf || !conf->enabled) return DECLINED; if(conf->prefixPath == NULL || !start_with(r->uri, conf->prefixPath)) return DECLINED; if(conf->stopPattern !=NULL && regexp_match(r->uri, conf->stopPattern)) return DECLINED; apr_hash_t* ugMap = apr_hash_make(r->pool); apr_hash_t* accessMap = apr_hash_make(r->pool); if(!parse_access_file(r, conf->accessFile, conf, ugMap, accessMap)) return 403; if(conf->debug) { // run in debug mode // print all users/groups and access information apr_hash_index_t* hi; char *key; apr_table_t *val; struct access_rec *arec; apr_array_header_t *arr; apr_table_entry_t *elts; int i; r->content_type=”text/plain”; ap_rprintf(r, “Parsed Users and Groups:\n”); hi = apr_hash_first(r->pool, ugMap); while(hi != NULL) { apr_hash_this(hi, (void *)&key, NULL, (void *)&val); ap_rprintf(r, “%s: “, key); arr = (apr_array_header_t *)apr_table_elts(val); elts = (apr_table_entry_t *)arr->elts; for(i=0; inelts; i++) { if(elts[i].key == NULL || elts[i].val == NULL) continue; if(elts[i].val[0] == ‘0′) { ap_rprintf(r, “@”); } ap_rprintf(r, “%s “, elts[i].key); } ap_rprintf(r, “\n”); hi = apr_hash_next(hi); } ap_rprintf(r, “Parsed Path Access:\n”); hi = apr_hash_first(r->pool, accessMap); while(hi != NULL) { apr_hash_this(hi, (void *)&key, NULL, (void *)&arec); ap_rprintf(r, “%s:\n”, key); while(arec != NULL) { if(arec->type == 0) ap_rprintf(r, “group:%s “, arec->name); else if(arec->type == 1) ap_rprintf(r, “user:%s “, arec->name); else ap_rprintf(r, “all “); ap_rprintf(r, “access:%d “, arec->access); ap_rprintf(r, “\n”); arec = arec->next; } ap_rprintf(r, “\n”); hi = apr_hash_next(hi); } } char *url = apr_pstrdup(r->pool, r->uri); // decode the url for some chinese characters url_decode(url); // analyze the access if(estimate_access(r, conf, url, ugMap, accessMap)) { if(conf->debug) { ap_rprintf(r, “%s have access on:%s\n”, r->user, r->uri); return OK; } return DECLINED; } else { if(conf->debug) { ap_rprintf(r, “%s don’t have access on:%s\n”, r->user, r->uri); return OK; } } return 403; } static const char *set_authSVN_enable(cmd_parms *cmd, void *mconfig, int arg) { authSVN_rec *conf = (authSVN_rec *) mconfig; conf->enabled = arg; return NULL; } static const char *set_authSVN_debug( cmd_parms *cmd, void *mconfig, int arg) { authSVN_rec *conf = (authSVN_rec *) mconfig; conf->debug = arg; return NULL; } static const char *set_prefix_path(cmd_parms *cmd, void *mconfig, const char *name) { authSVN_rec *conf = (authSVN_rec *) mconfig; if(strlen(name) <= 0) return "AuthSVNPrefixPath can not be null."; if(name[0] != '/' || name[strlen(name) - 1] != '/') return "AuthSVNPrefixPath must start and end with '/'."; conf->prefixPath = apr_pstrdup(cmd->pool, name); return NULL; } static const char *set_stop_pattern(cmd_parms *cmd, void *mconfig, const char *name) { authSVN_rec *conf = (authSVN_rec *) mconfig; if(strlen(name) <= 0) return "AuthSVNStopPattern can not be null."; conf->stopPattern = apr_pstrdup(cmd->pool, name); return NULL; } static const char *set_authSVN_accessFile(cmd_parms *cmd, void *mconfig, const char *name) { authSVN_rec *conf = (authSVN_rec *) mconfig; ap_configfile_t *f = NULL; apr_status_t status; if(strlen(name) <= 0) return "SVNAccessFile can not be null."; status = ap_pcfg_openfile(&f, cmd->pool, name); if (status != APR_SUCCESS) { return “Can not open given SVN access file.”; } ap_cfg_closefile(f); conf->accessFile = apr_pstrdup(cmd->pool, name); return NULL; } static const command_rec auth_cmds[] = { AP_INIT_FLAG(”EnableAuthSVN”, set_authSVN_enable, NULL, OR_FILEINFO, “enable authSVN or not.”), AP_INIT_FLAG(”DebugAuthSVN”, set_authSVN_debug, NULL, OR_FILEINFO, “debug authSVN or not.”), AP_INIT_TAKE1(”AuthSVNPrefixPath”, set_prefix_path, NULL, OR_FILEINFO, “set prefix path.”), AP_INIT_TAKE1(”AuthSVNStopPattern”, set_stop_pattern, NULL, OR_FILEINFO, “the url pattern we do not do the access checking.”), AP_INIT_TAKE1(”SVNAccessFile”, set_authSVN_accessFile, NULL, OR_FILEINFO, “set SVN access file.”), { NULL } }; static void register_hooks(apr_pool_t *p) { ap_hook_handler(authSVN_handler, NULL, NULL, APR_HOOK_FIRST); } module AP_MODULE_DECLARE_DATA authSVN_module = { STANDARD20_MODULE_STUFF, create_authSVN_dir_config, NULL, NULL, NULL, auth_cmds, register_hooks };