File: authz_svn_group.py

package info (click to toggle)
subversion 1.4.2dfsg1-3
  • links: PTS
  • area: main
  • in suites: etch
  • size: 37,284 kB
  • ctags: 32,888
  • sloc: ansic: 406,472; python: 38,378; sh: 15,438; cpp: 9,604; ruby: 8,313; perl: 5,308; java: 4,576; lisp: 3,860; xml: 3,298; makefile: 856
file content (182 lines) | stat: -rw-r--r-- 5,907 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/python
#
# Copyright 2005 Branko Cibej <brane@xbc.nu>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

'''mod_python authorization handler for mod_authz_svn groups.

   This handler reads group definitions from a mod_authz_svn access
   configuration file and does an authz check against that information.

   Supported Require directives:

     - Require valid-user
       Checks if the authenticated user is mentioned in any of the groups.
       Note that this is authorization, not authentication; so, a user may
       have been authenticated correctly, yet still fail this test if
       she is not mentioned in the authz config file.

     - Require group name...
       Check if the authenticated user is a member of any of the named
       groups.

     - Require user name...
       Ignored. The authentication handlers are supposed to check this.

   Configuration:

     <Location ...>
       PythonAuthzHandler authz_svn_group
       PythonOption AuthzSVNGroupFile /path/to/file
       PythonOption AuthzSVNGroupAuthoritative Yes/On/1|No/Off/0
       ...
     </Location>

     AuthzSVNGroupFile: Path to the mod_authz_svn configuration file.
     AuthzSVNGroupAuthoritative: If turned off, authz_svn_group.py will
       return DECLINED rather than HTTP_FORBIDDEN if a Require
       directive is not satisfied.
'''

import os, sys
import ConfigParser
from mod_python import apache

class __authz_info:
  '''Encapsulation of group info from the mod_authz_svn access file.'''

  def __init__(self, authz_file):
    '''Parse the SVN access file.'''
    self.__groups = {}
    self.__users = {}
    cfg = ConfigParser.ConfigParser()
    cfg.read(authz_file)
    if cfg.has_section('groups'):
      self.__init_groups(cfg)

  def __init_groups(self, cfg):
    '''Compute user and group membership.'''
    group_list = cfg.options('groups')
    group_map = {}
    for group in group_list:
      names = map(lambda x: x.strip(),
                  cfg.get('groups', group).split(','))
      group_map[group] = names
      for name in names:
        if not name.startswith('@'):
          self.__users[name] = None
    for group in group_list:
      self.__groups[group] = self.__expand_group_users(group, group_map)

  def __expand_group_users(self, group, group_map):
    '''Return the complete (recursive) list of users that belong to
    a particular group, as a map.'''
    users = {}
    for name in group_map[group]:
      if not name.startswith('@'):
        users[name] = None
      else:
        users.update(self.__expand_group_users(name[1:], group_map))
    return users

  def is_valid_user(self, user):
    '''Return True if the user is valid.'''
    return self.__users.has_key(user)

  def is_user_in_group(self, user, group):
    '''Return True if the user is in a particular group.'''
    return (self.__groups.has_key(group)
            and self.__groups[group].has_key(user))


class __config:
  '''Handler configuration'''

  AUTHZ_FILE = 'AuthzSVNGroupFile'
  AUTHORITATIVE = 'AuthzSVNGroupAuthoritative'

  def __init__(self, req):
    self.__authz_file = None
    self.__authoritative = True
    cfg = req.get_options()

    if cfg.has_key(self.AUTHZ_FILE):
      self.__authz_file = cfg[self.AUTHZ_FILE]
      if not os.path.exists(self.__authz_file):
        req.log_error(('%s: "%s" not found'
                       % (self.AUTHZ_FILE, self.__authz_file)),
                      apache.APLOG_ERR)
        raise apache.SERVER_RETURN, apache.HTTP_INTERNAL_SERVER_ERROR

    if cfg.has_key(self.AUTHORITATIVE):
      authcfg = cfg[self.AUTHORITATIVE].lower()
      if authcfg in ['yes', 'on', '1']:
        self.__authoritative = True
      elif authcfg in ['no', 'off', '0']:
        self.__authoritative = False
      else:
        req.log_error(('%s: invalid value "%s"'
                       % (self.AUTHORITATIVE, cfg[self.AUTHORITATIVE])),
                      apache.APLOG_ERR)
        raise apache.SERVER_RETURN, apache.HTTP_INTERNAL_SERVER_ERROR
    pass

  def authz_file(self):
    return self.__authz_file

  def authoritative(self):
    return self.__authoritative


def __init_authz_info(req, cfg):
  '''Initialize the global authz info if it is not available yet.
  Return False if this module is disabled.'''
  if not globals().has_key('__authz_svn_group_info'):
    if cfg.authz_file() is None:
      return False
    global __authz_svn_group_info
    __authz_svn_group_info = __authz_info(cfg.authz_file())
  return True


def authzhandler(req):
  '''The authorization handler.'''
  cfg = __config(req)
  if not __init_authz_info(req, cfg):
    return apache.DECLINED

  if cfg.authoritative():
    forbidden = apache.HTTP_FORBIDDEN
  else:
    forbidden = apache.DECLINED

  req.get_basic_auth_pw()
  for requires in req.requires():
    if requires == 'valid-user':
      if not __authz_svn_group_info.is_valid_user(req.user):
        return forbidden
    elif requires.startswith('group '):
      for group in requires.split()[1:]:
        if __authz_svn_group_info.is_user_in_group(req.user, group):
          break
      else:
        return forbidden
    elif requires.startswith('user '):
      pass                             # Handled by the authen handler
    else:
      req.log_error('Unknown directive "Require %s"' % requires,
                    apache.APLOG_ERR)
      return apache.HTTP_INTERNAL_SERVER_ERROR

  return apache.OK