39
|
1 # -*- coding: utf-8 -*-
|
|
2 #
|
|
3 # Copyright (C) 2004-2005 Edgewall Software
|
|
4 # Copyright (C) 2004 Francois Harvey <fharvey@securiweb.net>
|
|
5 # Copyright (C) 2005 Matthew Good <trac@matt-good.net>
|
|
6 # All rights reserved.
|
|
7 #
|
|
8 # This software is licensed as described in the file COPYING, which
|
|
9 # you should have received as part of this distribution. The terms
|
|
10 # are also available at http://trac.edgewall.com/license.html.
|
|
11 #
|
|
12 # This software consists of voluntary contributions made by many
|
|
13 # individuals. For the exact contribution history, see the revision
|
|
14 # history and logs, available at http://projects.edgewall.com/trac/.
|
|
15 #
|
|
16 # Author: Francois Harvey <fharvey@securiweb.net>
|
|
17 # Matthew Good <trac@matt-good.net>
|
|
18
|
|
19 from trac.config import Option
|
|
20 from trac.core import *
|
|
21 from trac.versioncontrol import Authorizer
|
|
22
|
|
23
|
|
24 class SvnAuthzOptions(Component):
|
|
25
|
|
26 authz_file = Option('trac', 'authz_file', '',
|
|
27 """Path to Subversion
|
|
28 [http://svnbook.red-bean.com/en/1.1/ch06s04.html#svn-ch-6-sect-4.4.2 authorization (authz) file]
|
|
29 """)
|
|
30
|
|
31 authz_module_name = Option('trac', 'authz_module_name', '',
|
|
32 """The module prefix used in the authz_file.""")
|
|
33
|
|
34
|
|
35 def SubversionAuthorizer(env, authname):
|
|
36 authz_file = env.config.get('trac', 'authz_file')
|
|
37 if not authz_file:
|
|
38 return Authorizer()
|
|
39
|
|
40 module_name = env.config.get('trac', 'authz_module_name')
|
|
41 db = env.get_db_cnx()
|
|
42 return RealSubversionAuthorizer(db, authname, module_name, authz_file)
|
|
43
|
|
44 def parent_iter(path):
|
|
45 path = path.strip('/')
|
|
46 if path:
|
|
47 path = '/' + path + '/'
|
|
48 else:
|
|
49 path = '/'
|
|
50
|
|
51 while 1:
|
|
52 yield path
|
|
53 if path == '/':
|
|
54 raise StopIteration()
|
|
55 path = path[:-1]
|
|
56 yield path
|
|
57 idx = path.rfind('/')
|
|
58 path = path[:idx + 1]
|
|
59
|
|
60
|
|
61 class RealSubversionAuthorizer(Authorizer):
|
|
62
|
|
63 auth_name = ''
|
|
64 module_name = ''
|
|
65 conf_authz = None
|
|
66
|
|
67 def __init__(self, db, auth_name, module_name, cfg_file, cfg_fp=None):
|
|
68 self.db = db
|
|
69 self.auth_name = auth_name
|
|
70 self.module_name = module_name
|
|
71
|
|
72 from ConfigParser import ConfigParser
|
|
73 self.conf_authz = ConfigParser()
|
|
74 if cfg_fp:
|
|
75 self.conf_authz.readfp(cfg_fp, cfg_file)
|
|
76 elif cfg_file:
|
|
77 self.conf_authz.read(cfg_file)
|
|
78
|
|
79 self.groups = self._groups()
|
|
80
|
|
81 def has_permission(self, path):
|
|
82 if path is None:
|
|
83 return 1
|
|
84
|
|
85 for p in parent_iter(path):
|
|
86 if self.module_name:
|
|
87 for perm in self._get_section(self.module_name + ':' + p):
|
|
88 if perm is not None:
|
|
89 return perm
|
|
90 for perm in self._get_section(p):
|
|
91 if perm is not None:
|
|
92 return perm
|
|
93
|
|
94 return 0
|
|
95
|
|
96 def has_permission_for_changeset(self, rev):
|
|
97 cursor = self.db.cursor()
|
|
98 cursor.execute("SELECT path FROM node_change WHERE rev=%s", (rev,))
|
|
99 for row in cursor:
|
|
100 if self.has_permission(row[0]):
|
|
101 return 1
|
|
102 return 0
|
|
103
|
|
104 # Internal API
|
|
105
|
|
106 def _groups(self):
|
|
107 if not self.conf_authz.has_section('groups'):
|
|
108 return []
|
|
109
|
|
110 grp_parents = {}
|
|
111 usr_grps = []
|
|
112
|
|
113 for group in self.conf_authz.options('groups'):
|
|
114 for member in self.conf_authz.get('groups', group).split(','):
|
|
115 member = member.strip()
|
|
116 if member == self.auth_name:
|
|
117 usr_grps.append(group)
|
|
118 elif member.startswith('@'):
|
|
119 grp_parents.setdefault(member[1:], []).append(group)
|
|
120
|
|
121 expanded = {}
|
|
122
|
|
123 def expand_group(group):
|
|
124 if group in expanded:
|
|
125 return
|
|
126 expanded[group] = True
|
|
127 for parent in grp_parents.get(group, []):
|
|
128 expand_group(parent)
|
|
129
|
|
130 for g in usr_grps:
|
|
131 expand_group(g)
|
|
132
|
|
133 # expand groups
|
|
134 return expanded.keys()
|
|
135
|
|
136 def _get_section(self, section):
|
|
137 if not self.conf_authz.has_section(section):
|
|
138 return
|
|
139
|
|
140 yield self._get_permission(section, self.auth_name)
|
|
141
|
|
142 group_perm = None
|
|
143 for g in self.groups:
|
|
144 p = self._get_permission(section, '@' + g)
|
|
145 if p is not None:
|
|
146 group_perm = p
|
|
147
|
|
148 if group_perm:
|
|
149 yield 1
|
|
150
|
|
151 yield group_perm
|
|
152
|
|
153 yield self._get_permission(section, '*')
|
|
154
|
|
155 def _get_permission(self, section, subject):
|
|
156 if self.conf_authz.has_option(section, subject):
|
|
157 return 'r' in self.conf_authz.get(section, subject)
|
|
158 return None
|