##// END OF EJS Templates
code garden...
marcink -
r1792:2afa6b8c beta
parent child Browse files
Show More
@@ -1,153 +1,156 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.changelog
3 rhodecode.controllers.changelog
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode authentication library for LDAP
6 RhodeCode authentication library for LDAP
7
7
8 :created_on: Created on Nov 17, 2010
8 :created_on: Created on Nov 17, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27
27
28 from rhodecode.lib.exceptions import LdapConnectionError, LdapUsernameError, \
28 from rhodecode.lib.exceptions import LdapConnectionError, LdapUsernameError, \
29 LdapPasswordError
29 LdapPasswordError
30
30
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
32
32
33
33
34 try:
34 try:
35 import ldap
35 import ldap
36 except ImportError:
36 except ImportError:
37 # means that python-ldap is not installed
37 # means that python-ldap is not installed
38 pass
38 pass
39
39
40
40
41 class AuthLdap(object):
41 class AuthLdap(object):
42
42
43 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
43 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
44 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
44 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
45 ldap_filter='(&(objectClass=user)(!(objectClass=computer)))',
45 ldap_filter='(&(objectClass=user)(!(objectClass=computer)))',
46 search_scope = 'SUBTREE', attr_login = 'uid'):
46 search_scope='SUBTREE', attr_login='uid'):
47 self.ldap_version = ldap_version
47 self.ldap_version = ldap_version
48 ldap_server_type = 'ldap'
48 ldap_server_type = 'ldap'
49
49
50 self.TLS_KIND = tls_kind
50 self.TLS_KIND = tls_kind
51
51
52 if self.TLS_KIND == 'LDAPS':
52 if self.TLS_KIND == 'LDAPS':
53 port = port or 689
53 port = port or 689
54 ldap_server_type = ldap_server_type + 's'
54 ldap_server_type = ldap_server_type + 's'
55
55
56 OPT_X_TLS_DEMAND = 2
56 OPT_X_TLS_DEMAND = 2
57 self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert,
57 self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert,
58 OPT_X_TLS_DEMAND)
58 OPT_X_TLS_DEMAND)
59 self.LDAP_SERVER_ADDRESS = server
59 self.LDAP_SERVER_ADDRESS = server
60 self.LDAP_SERVER_PORT = port
60 self.LDAP_SERVER_PORT = port
61
61
62 # USE FOR READ ONLY BIND TO LDAP SERVER
62 # USE FOR READ ONLY BIND TO LDAP SERVER
63 self.LDAP_BIND_DN = bind_dn
63 self.LDAP_BIND_DN = bind_dn
64 self.LDAP_BIND_PASS = bind_pass
64 self.LDAP_BIND_PASS = bind_pass
65
65
66 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
66 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
67 self.LDAP_SERVER_ADDRESS,
67 self.LDAP_SERVER_ADDRESS,
68 self.LDAP_SERVER_PORT)
68 self.LDAP_SERVER_PORT)
69
69
70 self.BASE_DN = base_dn
70 self.BASE_DN = base_dn
71 self.LDAP_FILTER = ldap_filter
71 self.LDAP_FILTER = ldap_filter
72 self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope)
72 self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope)
73 self.attr_login = attr_login
73 self.attr_login = attr_login
74
74
75 def authenticate_ldap(self, username, password):
75 def authenticate_ldap(self, username, password):
76 """Authenticate a user via LDAP and return his/her LDAP properties.
76 """
77 Authenticate a user via LDAP and return his/her LDAP properties.
77
78
78 Raises AuthenticationError if the credentials are rejected, or
79 Raises AuthenticationError if the credentials are rejected, or
79 EnvironmentError if the LDAP server can't be reached.
80 EnvironmentError if the LDAP server can't be reached.
80
81
81 :param username: username
82 :param username: username
82 :param password: password
83 :param password: password
83 """
84 """
84
85
85 from rhodecode.lib.helpers import chop_at
86 from rhodecode.lib.helpers import chop_at
86
87
87 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
88 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
88
89
89 if not password:
90 if not password:
90 log.debug("Attempt to authenticate LDAP user with blank password rejected.")
91 log.debug("Attempt to authenticate LDAP user "
92 "with blank password rejected.")
91 raise LdapPasswordError()
93 raise LdapPasswordError()
92 if "," in username:
94 if "," in username:
93 raise LdapUsernameError("invalid character in username: ,")
95 raise LdapUsernameError("invalid character in username: ,")
94 try:
96 try:
95 if hasattr(ldap,'OPT_X_TLS_CACERTDIR'):
97 if hasattr(ldap, 'OPT_X_TLS_CACERTDIR'):
96 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR,
98 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR,
97 '/etc/openldap/cacerts')
99 '/etc/openldap/cacerts')
98 ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
100 ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
99 ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
101 ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
100 ldap.set_option(ldap.OPT_TIMEOUT, 20)
102 ldap.set_option(ldap.OPT_TIMEOUT, 20)
101 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
103 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
102 ldap.set_option(ldap.OPT_TIMELIMIT, 15)
104 ldap.set_option(ldap.OPT_TIMELIMIT, 15)
103 if self.TLS_KIND != 'PLAIN':
105 if self.TLS_KIND != 'PLAIN':
104 ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
106 ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
105 server = ldap.initialize(self.LDAP_SERVER)
107 server = ldap.initialize(self.LDAP_SERVER)
106 if self.ldap_version == 2:
108 if self.ldap_version == 2:
107 server.protocol = ldap.VERSION2
109 server.protocol = ldap.VERSION2
108 else:
110 else:
109 server.protocol = ldap.VERSION3
111 server.protocol = ldap.VERSION3
110
112
111 if self.TLS_KIND == 'START_TLS':
113 if self.TLS_KIND == 'START_TLS':
112 server.start_tls_s()
114 server.start_tls_s()
113
115
114 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
116 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
115 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
117 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
116
118
117 filt = '(&%s(%s=%s))' % (self.LDAP_FILTER, self.attr_login,
119 filter_ = '(&%s(%s=%s))' % (self.LDAP_FILTER, self.attr_login,
118 username)
120 username)
119 log.debug("Authenticating %r filt %s at %s", self.BASE_DN,
121 log.debug("Authenticating %r filter %s at %s", self.BASE_DN,
120 filt, self.LDAP_SERVER)
122 filter_, self.LDAP_SERVER)
121 lobjects = server.search_ext_s(self.BASE_DN, self.SEARCH_SCOPE,
123 lobjects = server.search_ext_s(self.BASE_DN, self.SEARCH_SCOPE,
122 filt)
124 filter_)
123
125
124 if not lobjects:
126 if not lobjects:
125 raise ldap.NO_SUCH_OBJECT()
127 raise ldap.NO_SUCH_OBJECT()
126
128
127 for (dn, _attrs) in lobjects:
129 for (dn, _attrs) in lobjects:
128 if dn is None:
130 if dn is None:
129 continue
131 continue
130
132
131 try:
133 try:
134 log.debug('Trying simple bind with %s' % dn)
132 server.simple_bind_s(dn, password)
135 server.simple_bind_s(dn, password)
133 attrs = server.search_ext_s(dn, ldap.SCOPE_BASE,
136 attrs = server.search_ext_s(dn, ldap.SCOPE_BASE,
134 '(objectClass=*)')[0][1]
137 '(objectClass=*)')[0][1]
135 break
138 break
136
139
137 except ldap.INVALID_CREDENTIALS, e:
140 except ldap.INVALID_CREDENTIALS:
138 log.debug("LDAP rejected password for user '%s' (%s): %s",
141 log.debug("LDAP rejected password for user '%s' (%s): %s",
139 uid, username, dn)
142 uid, username, dn)
140
143
141 else:
144 else:
142 log.debug("No matching LDAP objects for authentication "
145 log.debug("No matching LDAP objects for authentication "
143 "of '%s' (%s)", uid, username)
146 "of '%s' (%s)", uid, username)
144 raise LdapPasswordError()
147 raise LdapPasswordError()
145
148
146 except ldap.NO_SUCH_OBJECT, e:
149 except ldap.NO_SUCH_OBJECT:
147 log.debug("LDAP says no such user '%s' (%s)", uid, username)
150 log.debug("LDAP says no such user '%s' (%s)", uid, username)
148 raise LdapUsernameError()
151 raise LdapUsernameError()
149 except ldap.SERVER_DOWN, e:
152 except ldap.SERVER_DOWN:
150 raise LdapConnectionError("LDAP can't access "
153 raise LdapConnectionError("LDAP can't access "
151 "authentication server")
154 "authentication server")
152
155
153 return (dn, attrs)
156 return (dn, attrs)
@@ -1,129 +1,130 b''
1 import sys
1 import sys
2 from rhodecode import get_version
2 from rhodecode import get_version
3 from rhodecode import __platform__
3 from rhodecode import __platform__
4 from rhodecode import __license__
4 from rhodecode import __license__
5 from rhodecode import PLATFORM_OTHERS
5 from rhodecode import PLATFORM_OTHERS
6
6
7 py_version = sys.version_info
7 py_version = sys.version_info
8
8
9 if py_version < (2, 5):
9 if py_version < (2, 5):
10 raise Exception('RhodeCode requires python 2.5 or later')
10 raise Exception('RhodeCode requires python 2.5 or later')
11
11
12 requirements = [
12 requirements = [
13 "Pylons==1.0.0",
13 "Pylons==1.0.0",
14 "Beaker==1.6.2",
14 "Beaker==1.6.2",
15 "WebHelpers>=1.2",
15 "WebHelpers>=1.2",
16 "formencode==1.2.4",
16 "formencode==1.2.4",
17 "SQLAlchemy==0.7.3",
17 "SQLAlchemy==0.7.3",
18 "Mako==0.5.0",
18 "Mako==0.5.0",
19 "pygments>=1.4",
19 "pygments>=1.4",
20 "mercurial>=2.0,<2.1",
20 "mercurial>=2.0,<2.1",
21 "whoosh<1.8",
21 "whoosh<1.8",
22 "celery>=2.2.5,<2.3",
22 "celery>=2.2.5,<2.3",
23 "babel",
23 "babel",
24 "python-dateutil>=1.5.0,<2.0.0",
24 "python-dateutil>=1.5.0,<2.0.0",
25 "dulwich>=0.8.0,<0.9.0",
25 "dulwich>=0.8.0,<0.9.0",
26 "vcs>=0.2.3.dev",
26 "vcs>=0.2.3.dev",
27 "webob==1.0.8",
27 "webob==1.0.8",
28 "markdown==2.0.3",
28 "markdown==2.0.3",
29 "docutils==0.8.1",
29 "docutils==0.8.1",
30 ]
30 ]
31
31
32 dependency_links = [
32 dependency_links = [
33 "https://secure.rhodecode.org/vcs/archive/default.zip#egg=vcs-0.2.3.dev",
33 "https://secure.rhodecode.org/vcs/archive/default.zip#egg=vcs-0.2.3.dev",
34 "https://bitbucket.org/marcinkuzminski/vcs/get/default.zip#egg=vcs-0.2.3.dev",
34 "https://bitbucket.org/marcinkuzminski/vcs/get/default.zip#egg=vcs-0.2.3.dev",
35 ]
35 ]
36
36
37 classifiers = ['Development Status :: 4 - Beta',
37 classifiers = ['Development Status :: 4 - Beta',
38 'Environment :: Web Environment',
38 'Environment :: Web Environment',
39 'Framework :: Pylons',
39 'Framework :: Pylons',
40 'Intended Audience :: Developers',
40 'Intended Audience :: Developers',
41 'License :: OSI Approved :: GNU General Public License (GPL)',
41 'License :: OSI Approved :: GNU General Public License (GPL)',
42 'Operating System :: OS Independent',
42 'Operating System :: OS Independent',
43 'Programming Language :: Python',
43 'Programming Language :: Python',
44 'Programming Language :: Python :: 2.5',
44 'Programming Language :: Python :: 2.5',
45 'Programming Language :: Python :: 2.6',
45 'Programming Language :: Python :: 2.6',
46 'Programming Language :: Python :: 2.7', ]
46 'Programming Language :: Python :: 2.7', ]
47
47
48 if py_version < (2, 6):
48 if py_version < (2, 6):
49 requirements.append("simplejson")
49 requirements.append("simplejson")
50 requirements.append("pysqlite")
50 requirements.append("pysqlite")
51
51
52 if __platform__ in PLATFORM_OTHERS:
52 if __platform__ in PLATFORM_OTHERS:
53 requirements.append("py-bcrypt")
53 requirements.append("py-bcrypt")
54
54
55
55
56 #additional files from project that goes somewhere in the filesystem
56 # additional files from project that goes somewhere in the filesystem
57 #relative to sys.prefix
57 # relative to sys.prefix
58 data_files = []
58 data_files = []
59
59
60 #additional files that goes into package itself
60 # additional files that goes into package itself
61 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
61 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
62
62
63 description = ('Mercurial repository browser/management with '
63 description = ('Mercurial repository browser/management with '
64 'build in push/pull server and full text search')
64 'build in push/pull server and full text search')
65 keywords = ' '.join(['rhodecode', 'rhodiumcode', 'mercurial', 'git',
65 keywords = ' '.join(['rhodecode', 'rhodiumcode', 'mercurial', 'git',
66 'code review', 'repo groups', 'ldap'
66 'repository management', 'hgweb replacement'
67 'repository management', 'hgweb replacement'
67 'hgwebdir', 'gitweb replacement', 'serving hgweb', ])
68 'hgwebdir', 'gitweb replacement', 'serving hgweb', ])
68 #long description
69 # long description
69 try:
70 try:
70 readme_file = 'README.rst'
71 readme_file = 'README.rst'
71 changelog_file = 'docs/changelog.rst'
72 changelog_file = 'docs/changelog.rst'
72 long_description = open(readme_file).read() + '\n\n' + \
73 long_description = open(readme_file).read() + '\n\n' + \
73 open(changelog_file).read()
74 open(changelog_file).read()
74
75
75 except IOError, err:
76 except IOError, err:
76 sys.stderr.write("[WARNING] Cannot find file specified as "
77 sys.stderr.write("[WARNING] Cannot find file specified as "
77 "long_description (%s)\n or changelog (%s) skipping that file" \
78 "long_description (%s)\n or changelog (%s) skipping that file" \
78 % (readme_file, changelog_file))
79 % (readme_file, changelog_file))
79 long_description = description
80 long_description = description
80
81
81
82
82 try:
83 try:
83 from setuptools import setup, find_packages
84 from setuptools import setup, find_packages
84 except ImportError:
85 except ImportError:
85 from ez_setup import use_setuptools
86 from ez_setup import use_setuptools
86 use_setuptools()
87 use_setuptools()
87 from setuptools import setup, find_packages
88 from setuptools import setup, find_packages
88 #packages
89 # packages
89 packages = find_packages(exclude=['ez_setup'])
90 packages = find_packages(exclude=['ez_setup'])
90
91
91 setup(
92 setup(
92 name='RhodeCode',
93 name='RhodeCode',
93 version=get_version(),
94 version=get_version(),
94 description=description,
95 description=description,
95 long_description=long_description,
96 long_description=long_description,
96 keywords=keywords,
97 keywords=keywords,
97 license=__license__,
98 license=__license__,
98 author='Marcin Kuzminski',
99 author='Marcin Kuzminski',
99 author_email='marcin@python-works.com',
100 author_email='marcin@python-works.com',
100 dependency_links=dependency_links,
101 dependency_links=dependency_links,
101 url='http://rhodecode.org',
102 url='http://rhodecode.org',
102 install_requires=requirements,
103 install_requires=requirements,
103 classifiers=classifiers,
104 classifiers=classifiers,
104 setup_requires=["PasteScript>=1.6.3"],
105 setup_requires=["PasteScript>=1.6.3"],
105 data_files=data_files,
106 data_files=data_files,
106 packages=packages,
107 packages=packages,
107 include_package_data=True,
108 include_package_data=True,
108 test_suite='nose.collector',
109 test_suite='nose.collector',
109 package_data=package_data,
110 package_data=package_data,
110 message_extractors={'rhodecode': [
111 message_extractors={'rhodecode': [
111 ('**.py', 'python', None),
112 ('**.py', 'python', None),
112 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
113 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
113 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
114 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
114 ('public/**', 'ignore', None)]},
115 ('public/**', 'ignore', None)]},
115 zip_safe=False,
116 zip_safe=False,
116 paster_plugins=['PasteScript', 'Pylons'],
117 paster_plugins=['PasteScript', 'Pylons'],
117 entry_points="""
118 entry_points="""
118 [paste.app_factory]
119 [paste.app_factory]
119 main = rhodecode.config.middleware:make_app
120 main = rhodecode.config.middleware:make_app
120
121
121 [paste.app_install]
122 [paste.app_install]
122 main = pylons.util:PylonsInstaller
123 main = pylons.util:PylonsInstaller
123
124
124 [paste.global_paster_command]
125 [paste.global_paster_command]
125 make-index = rhodecode.lib.indexers:MakeIndex
126 make-index = rhodecode.lib.indexers:MakeIndex
126 upgrade-db = rhodecode.lib.dbmigrate:UpgradeDb
127 upgrade-db = rhodecode.lib.dbmigrate:UpgradeDb
127 celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand
128 celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand
128 """,
129 """,
129 )
130 )
General Comments 0
You need to be logged in to leave comments. Login now