source: TracAccountManager/0.10/acct_mgr/htfile.py @ 13

Last change on this file since 13 was 2, checked in by guillaume, 17 years ago

Ajout TracAccountManager en français

File size: 5.7 KB
Line 
1# -*- coding: utf8 -*-
2#
3# Copyright (C) 2005,2006,2007 Matthew Good <trac@matt-good.net>
4#
5# "THE BEER-WARE LICENSE" (Revision 42):
6# <trac@matt-good.net> wrote this file.  As long as you retain this notice you
7# can do whatever you want with this stuff. If we meet some day, and you think
8# this stuff is worth it, you can buy me a beer in return.   Matthew Good
9#
10# Author: Matthew Good <trac@matt-good.net>
11
12from __future__ import generators
13
14import errno
15import os.path
16import fileinput
17
18from trac.core import *
19from trac.config import Option
20
21from api import IPasswordStore
22from pwhash import htpasswd, htdigest
23
24class _RelativePathOption(Option):
25
26    def __get__(self, instance, owner):
27        if instance is None:
28            return self
29        path = super(_RelativePathOption, self).__get__(instance, owner)
30        return os.path.normpath(os.path.join(instance.env.path, path))
31
32
33class AbstractPasswordFileStore(Component):
34    """Base class for managing password files such as Apache's htpasswd and
35    htdigest formats.
36
37    See the concrete sub-classes for usage information.
38    """
39
40    filename = _RelativePathOption('account-manager', 'password_file')
41
42    def has_user(self, user):
43        return user in self.get_users()
44
45    def get_users(self):
46        filename = self.filename
47        if not os.path.exists(filename):
48            return []
49        return self._get_users(filename)
50
51    def set_password(self, user, password):
52        user = user.encode('utf-8')
53        password = password.encode('utf-8')
54        return not self._update_file(self.prefix(user),
55                                     self.userline(user, password))
56
57    def delete_user(self, user):
58        user = user.encode('utf-8')
59        return self._update_file(self.prefix(user), None)
60
61    def check_password(self, user, password):
62        filename = self.filename
63        if not os.path.exists(filename):
64            return False
65        user = user.encode('utf-8')
66        password = password.encode('utf-8')
67        prefix = self.prefix(user)
68        fd = file(filename)
69        try:
70            for line in fd:
71                if line.startswith(prefix):
72                    return self._check_userline(user, password,
73                                                line[len(prefix):].rstrip('\n'))
74        finally:
75            fd.close()
76        return False
77
78    def _update_file(self, prefix, userline):
79        """If `userline` is empty the line starting with `prefix` is
80        removed from the user file.  Otherwise the line starting with `prefix`
81        is updated to `userline`.  If no line starts with `prefix` the
82        `userline` is appended to the file.
83
84        Returns `True` if a line matching `prefix` was updated,
85        `False` otherwise.
86        """
87        filename = self.filename
88        matched = False
89        try:
90            for line in fileinput.input(str(filename), inplace=True):
91                if line.startswith(prefix):
92                    if not matched and userline:
93                        print userline
94                    matched = True
95                else:
96                    print line,
97        except EnvironmentError, e:
98            if e.errno == errno.ENOENT:
99                pass # ignore when file doesn't exist and create it below
100            elif e.errno == errno.EACCES:
101                raise TracError(u'Le fichier des mots de passe ne peut être '
102                                u'mis à jour. Trac a besoin des accés en lecture'
103                                u' et écriture aussi bien sur ce fichier que '
104                                u'sur son dossier parent.')
105            else:
106                raise
107        if not matched and userline:
108            f = open(filename, 'a')
109            try:
110                print >>f, userline
111            finally:
112                f.close()
113        return matched
114
115
116class HtPasswdStore(AbstractPasswordFileStore):
117    """Manages user accounts stored in Apache's htpasswd format.
118
119    To use this implementation add the following configuration section to
120    trac.ini:
121    {{{
122    [account-manager]
123    password_store = HtPasswdStore
124    password_file = /path/to/trac.htpasswd
125    }}}
126    """
127
128    implements(IPasswordStore)
129
130    def config_key(self):
131        return 'htpasswd'
132
133    def prefix(self, user):
134        return user + ':'
135
136    def userline(self, user, password):
137        return self.prefix(user) + htpasswd(password)
138
139    def _check_userline(self, user, password, suffix):
140        return suffix == htpasswd(password, suffix)
141
142    def _get_users(self, filename):
143        f = open(filename)
144        for line in f:
145            user = line.split(':', 1)[0]
146            if user:
147                yield user.decode('utf-8')
148
149
150class HtDigestStore(AbstractPasswordFileStore):
151    """Manages user accounts stored in Apache's htdigest format.
152
153    To use this implementation add the following configuration section to
154    trac.ini:
155    {{{
156    [account-manager]
157    password_store = HtDigestStore
158    password_file = /path/to/trac.htdigest
159    htdigest_realm = TracDigestRealm
160    }}}
161    """
162
163
164    implements(IPasswordStore)
165
166    realm = Option('account-manager', 'htdigest_realm')
167
168    def config_key(self):
169        return 'htdigest'
170
171    def prefix(self, user):
172        return '%s:%s:' % (user, self.realm)
173
174    def userline(self, user, password):
175        return self.prefix(user) + htdigest(user, self.realm, password)
176
177    def _check_userline(self, user, password, suffix):
178        return suffix == htdigest(user, self.realm, password)
179
180    def _get_users(self, filename):
181        _realm = self.realm
182        f = open(filename)
183        for line in f:
184            args = line.split(':')[:2]
185            if len(args) == 2:
186                user, realm = args
187                if realm == _realm and user:
188                    yield user.decode('utf-8')
189
Note: See TracBrowser for help on using the repository browser.