下面公布一个目前在我们公司使用的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 };
相关推荐
这个集成过程可能会遇到一些挑战,例如权限问题、URL重写规则的配置等,但按照上述步骤,大多数用户应该能够成功建立一个运行在Windows上的Subversion、Apache和ViewVC环境。记得在安装和配置过程中,始终检查错误...
### Linux开发环境工具详解 ...以上工具构成了一个完整的Linux开发环境,无论是初学者还是经验丰富的开发者都能从中找到合适的工具来提高工作效率。希望本文能够帮助Linux新手们更好地搭建和发展自己的开发环境。
在本文档中,作者详细介绍了如何在Linux Fedora系统上搭建SVN(Subversion)服务器环境,以便进行软件开发和版本控制。以下是对整个过程的详细解释: 1. **准备工作** 在安装好Fedora操作系统后,首先需要通过`yum...
Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ...
Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ...
ViewVC提供了一个友好的用户界面,通过浏览器访问,可以查看版本控制系统的各种信息,包括文件历史、分支结构、差异比较等。它支持多种语言,并且可以集成到各种Web服务器中,如Apache和IIS。 2. **源码结构** ...
从 CollabNetSubversion-server-1.7.1-3-Win32 版本的安装包开始, ViewVC 已经从安装包剔除, 照 CollabNet 的意思, 他们是想用户下载他们又臭又长的 CollabNet Subversion Edge. 安装包内增加了 svn-python2.7 C:\...
总之,ViewVC是Subversion和CVS用户的重要辅助工具,它提供了一个直观的图形界面,简化了版本控制操作,提升了团队协作效率。而"1.2-dev"版本则为开发者和早期采用者提供了探索和测试最新特性的机会。
ViewVC(Visual Version Control System)是一款开源的Web接口软件,用于查看和浏览版本控制系统,如SVN(Subversion)和CVS(Concurrent Versions System)。它提供了用户友好的界面,使得开发者和团队成员能够轻松...
在本篇文章中,我们将详细介绍如何搭建一个结合了版本控制系统Subversion(简称SVN)与Web前端查看工具ViewVC的服务器环境。这一组合可以方便地为团队提供版本控制服务及代码浏览器功能,极大地提高了开发效率与协作...
在IT领域,特别是版本控制系统中,Subversion(简称SVN)是一个广泛使用的工具,它允许团队协作并管理代码和其他文件的版本。Trac是一个开源的项目管理和问题跟踪系统,它可以与SVN集成,提供了一个直观的界面来查看...
在 apache2 中运行 viewvc 做什么/配置: Dockerfile 期望当前目录中的文件“subversion_conf.tar”并将其解压缩到 www-data users 目录中。 此存档应包含一个子目录“.subversion”,其中包含保存的身份验证数据...
- `child_terminate`: 当PHP作为Apache的一个模块运行时,此配置决定了是否允许PHP使用`apache_child_terminate()`函数在子进程中终止进程。对于Unix平台下安装为Apache 1.3模块的PHP来说,默认应设置为`Off`。 - `...
ViewVC,原名为ViewCVS,...总的来说,ViewVC是一个强大且灵活的工具,可以帮助开发者和团队高效地管理和跟踪版本控制系统的数据,提升协作效率。它的开源性质也意味着用户可以自由地定制和扩展,以适应各种工作场景。
ViewVC-版本控制浏览器界面ViewVC是CVS和Subversion版本控制存储库的浏览器界面。 它生成模板化HTML,以显示可导航的目录,修订和更改日志列表。 它可以显示文件的特定版本以及这些版本之间的差异。 基本上,ViewVC...
### SVN使用手册知识点详解 #### 1. SVN介绍 ##### 1.1 版本控制 版本控制是一种软件开发过程中的重要技术,它允许开发者跟踪和管理代码的变化历史。通过版本控制系统,开发者可以在任何时候恢复到之前的某个版本...
Trac 是一个轻量级的项目管理工具,主要用于跟踪软件开发过程中的问题、变更记录等信息,并且可以与 SVN 等版本控制系统紧密结合,实现项目的高效管理。 #### 四、集成安装步骤 为了实现 Collabnet Subversion ...
ViewVC -- Viewing the content of CVS/SVN repositories with a Webbrowser. Please read the file INSTALL for more information. And see windows/README for more information on running ViewVC on Microsoft...
也包含了 ViewVC 1.0.12 C:\Program Files\CollabNet\Subversion Server>svnserve --version svnserve, version 1.7.1 (r1186859) compiled Oct 21 2011, 02:00:39 C:\Program Files\CollabNet\Subversion ...
1.3.4.0:使用事件页面。 --- 这是用于测试Chrome页面加载时间性能数据的扩展。 要使用它,当您启动Chrome浏览器时,它需要命令行标志--enable-基准。 对于浏览器的非常新的构建,也使用--enable-stats-table。 ...