##// END OF EJS Templates
release: merge back stable branch into default
marcink -
r2747:f690c910 merge default
parent child Browse files
Show More
@@ -0,0 +1,48 b''
1 |RCE| 4.12.1 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2018-05-15
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13
14
15 General
16 ^^^^^^^
17
18 - SVN: execute web-based hooks for SVN so integration framework work also via
19 web based editor.
20 - ReCaptcha: adjust for v2 that is the only left one supported since 1st of May.
21 - JIRA integration: add support for proxy server connection to JIRA server.
22
23
24 Security
25 ^^^^^^^^
26
27
28
29 Performance
30 ^^^^^^^^^^^
31
32
33
34 Fixes
35 ^^^^^
36
37 - SVN: make hooks safer and fully backward compatible. In certain old setups
38 new integration for SVN could make problems. We use a safer hooks now that
39 shouldn't break usage of older SVN and still provide required functionality
40 for integration framework
41 - LDAP: use connection ping only in case of single server.
42 - Repository feed: fix path-based permissions condition on caching element.
43
44
45 Upgrade notes
46 ^^^^^^^^^^^^^
47
48 - Scheduled release addressing found problems reported by users.
@@ -36,3 +36,4 b' 194c74f33e32bbae6fc4d71ec5a999cff3c13605'
36 36 8fbd8b0c3ddc2fa4ac9e4ca16942a03eb593df2d v4.11.5
37 37 f0609aa5d5d05a1ca2f97c3995542236131c9d8a v4.11.6
38 38 b5b30547d90d2e088472a70c84878f429ffbf40d v4.12.0
39 9072253aa8894d20c00b4a43dc61c2168c1eff94 v4.12.1
@@ -9,6 +9,7 b' Release Notes'
9 9 .. toctree::
10 10 :maxdepth: 1
11 11
12 release-notes-4.12.1.rst
12 13 release-notes-4.12.0.rst
13 14 release-notes-4.11.6.rst
14 15 release-notes-4.11.5.rst
@@ -152,13 +152,14 b' class RepoFeedView(RepoAppView):'
152 152 return _generate_feed()
153 153
154 154 if self.path_filter.is_enabled:
155 mime_type, feed = _generate_feed()
156 else:
155 157 invalidator_context = CacheKey.repo_context_cache(
156 _generate_feed_and_cache, self.db_repo_name, CacheKey.CACHE_TYPE_ATOM)
158 _generate_feed_and_cache, self.db_repo_name,
159 CacheKey.CACHE_TYPE_ATOM)
157 160 with invalidator_context as context:
158 161 context.invalidate()
159 162 mime_type, feed = context.compute()
160 else:
161 mime_type, feed = _generate_feed()
162 163
163 164 response = Response(feed)
164 165 response.content_type = mime_type
@@ -204,14 +205,15 b' class RepoFeedView(RepoAppView):'
204 205 return _generate_feed()
205 206
206 207 if self.path_filter.is_enabled:
208 mime_type, feed = _generate_feed()
209 else:
207 210 invalidator_context = CacheKey.repo_context_cache(
208 _generate_feed_and_cache, self.db_repo_name, CacheKey.CACHE_TYPE_RSS)
211 _generate_feed_and_cache, self.db_repo_name,
212 CacheKey.CACHE_TYPE_RSS)
209 213
210 214 with invalidator_context as context:
211 215 context.invalidate()
212 216 mime_type, feed = context.compute()
213 else:
214 mime_type, feed = _generate_feed()
215 217
216 218 response = Response(feed)
217 219 response.content_type = mime_type
@@ -21,7 +21,8 b''
21 21 """
22 22 Authentication modules
23 23 """
24
24 import socket
25 import string
25 26 import colander
26 27 import copy
27 28 import logging
@@ -31,14 +32,13 b' import warnings'
31 32 import functools
32 33
33 34 from pyramid.threadlocal import get_current_registry
34 from zope.cachedescriptors.property import Lazy as LazyProperty
35 35
36 36 from rhodecode.authentication.interface import IAuthnPluginRegistry
37 37 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
38 38 from rhodecode.lib import caches
39 39 from rhodecode.lib.auth import PasswordGenerator, _RhodeCodeCryptoBCrypt
40 from rhodecode.lib.utils2 import safe_int
41 from rhodecode.lib.utils2 import safe_str
40 from rhodecode.lib.utils2 import safe_int, safe_str
41 from rhodecode.lib.exceptions import LdapConnectionError
42 42 from rhodecode.model.db import User
43 43 from rhodecode.model.meta import Session
44 44 from rhodecode.model.settings import SettingsModel
@@ -556,6 +556,63 b' class RhodeCodeExternalAuthPlugin(RhodeC'
556 556 return auth
557 557
558 558
559 class AuthLdapBase(object):
560
561 @classmethod
562 def _build_servers(cls, ldap_server_type, ldap_server, port):
563 def host_resolver(host, port, full_resolve=True):
564 """
565 Main work for this function is to prevent ldap connection issues,
566 and detect them early using a "greenified" sockets
567 """
568 host = host.strip()
569 if not full_resolve:
570 return '{}:{}'.format(host, port)
571
572 log.debug('LDAP: Resolving IP for LDAP host %s', host)
573 try:
574 ip = socket.gethostbyname(host)
575 log.debug('Got LDAP server %s ip %s', host, ip)
576 except Exception:
577 raise LdapConnectionError(
578 'Failed to resolve host: `{}`'.format(host))
579
580 log.debug('LDAP: Checking if IP %s is accessible', ip)
581 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
582 try:
583 s.connect((ip, int(port)))
584 s.shutdown(socket.SHUT_RD)
585 except Exception:
586 raise LdapConnectionError(
587 'Failed to connect to host: `{}:{}`'.format(host, port))
588
589 return '{}:{}'.format(host, port)
590
591 if len(ldap_server) == 1:
592 # in case of single server use resolver to detect potential
593 # connection issues
594 full_resolve = True
595 else:
596 full_resolve = False
597
598 return ', '.join(
599 ["{}://{}".format(
600 ldap_server_type,
601 host_resolver(host, port, full_resolve=full_resolve))
602 for host in ldap_server])
603
604 @classmethod
605 def _get_server_list(cls, servers):
606 return map(string.strip, servers.split(','))
607
608 @classmethod
609 def get_uid(cls, username, server_addresses):
610 uid = username
611 for server_addr in server_addresses:
612 uid = chop_at(username, "@%s" % server_addr)
613 return uid
614
615
559 616 def loadplugin(plugin_id):
560 617 """
561 618 Loads and returns an instantiated authentication plugin.
@@ -613,7 +670,7 b' def authenticate(username, password, env'
613 670 authn_registry = get_authn_registry(registry)
614 671 plugins_to_check = authn_registry.get_plugins_for_authentication()
615 672 log.debug('Starting ordered authentication chain using %s plugins',
616 plugins_to_check)
673 [x.name for x in plugins_to_check])
617 674 for plugin in plugins_to_check:
618 675 plugin.set_auth_type(auth_type)
619 676 plugin.set_calling_scope_repo(acl_repo_name)
@@ -0,0 +1,19 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2012-2018 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
@@ -22,16 +22,13 b''
22 22 RhodeCode authentication plugin for LDAP
23 23 """
24 24
25 import socket
26 import re
27 import colander
28 25 import logging
29 26 import traceback
30 import string
31 27
28 import colander
32 29 from rhodecode.translation import _
33 30 from rhodecode.authentication.base import (
34 RhodeCodeExternalAuthPlugin, chop_at, hybrid_property)
31 RhodeCodeExternalAuthPlugin, AuthLdapBase, hybrid_property)
35 32 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
36 33 from rhodecode.authentication.routes import AuthnPluginResourceBase
37 34 from rhodecode.lib.colander_utils import strip_whitespace
@@ -200,40 +197,7 b' class LdapSettingsSchema(AuthnPluginSett'
200 197 widget='string')
201 198
202 199
203 class AuthLdap(object):
204
205 def _build_servers(self):
206 def host_resolver(host, port):
207 """
208 Main work for this function is to prevent ldap connection issues,
209 and detect them early using a "greenified" sockets
210 """
211 host = host.strip()
212
213 log.info('Resolving LDAP host %s', host)
214 try:
215 ip = socket.gethostbyname(host)
216 log.info('Got LDAP server %s ip %s', host, ip)
217 except Exception:
218 raise LdapConnectionError(
219 'Failed to resolve host: `{}`'.format(host))
220
221 log.info('Checking LDAP IP access %s', ip)
222 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
223 try:
224 s.connect((ip, int(port)))
225 s.shutdown(socket.SHUT_RD)
226 except Exception:
227 raise LdapConnectionError(
228 'Failed to connect to host: `{}:{}`'.format(host, port))
229
230 return '{}:{}'.format(host, port)
231
232 port = self.LDAP_SERVER_PORT
233 return ', '.join(
234 ["{}://{}".format(
235 self.ldap_server_type, host_resolver(host, port))
236 for host in self.SERVER_ADDRESSES])
200 class AuthLdap(AuthLdapBase):
237 201
238 202 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
239 203 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
@@ -256,8 +220,9 b' class AuthLdap(object):'
256 220 OPT_X_TLS_DEMAND = 2
257 221 self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert,
258 222 OPT_X_TLS_DEMAND)
223 self.LDAP_SERVER = server
259 224 # split server into list
260 self.SERVER_ADDRESSES = server.split(',')
225 self.SERVER_ADDRESSES = self._get_server_list(server)
261 226 self.LDAP_SERVER_PORT = port
262 227
263 228 # USE FOR READ ONLY BIND TO LDAP SERVER
@@ -265,13 +230,12 b' class AuthLdap(object):'
265 230
266 231 self.LDAP_BIND_DN = safe_str(bind_dn)
267 232 self.LDAP_BIND_PASS = safe_str(bind_pass)
268 self.LDAP_SERVER = self._build_servers()
233
269 234 self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope)
270 235 self.BASE_DN = safe_str(base_dn)
271 236 self.LDAP_FILTER = safe_str(ldap_filter)
272 237
273 238 def _get_ldap_conn(self):
274 log.debug('initializing LDAP connection to:%s', self.LDAP_SERVER)
275 239
276 240 if self.debug:
277 241 ldap.set_option(ldap.OPT_DEBUG_LEVEL, 255)
@@ -285,7 +249,10 b' class AuthLdap(object):'
285 249 ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
286 250
287 251 # init connection now
288 ldap_conn = ldap.initialize(self.LDAP_SERVER)
252 ldap_servers = self._build_servers(
253 self.ldap_server_type, self.SERVER_ADDRESSES, self.LDAP_SERVER_PORT)
254 log.debug('initializing LDAP connection to:%s', ldap_servers)
255 ldap_conn = ldap.initialize(ldap_servers)
289 256 ldap_conn.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout)
290 257 ldap_conn.set_option(ldap.OPT_TIMEOUT, self.timeout)
291 258 ldap_conn.timeout = self.timeout
@@ -305,12 +272,6 b' class AuthLdap(object):'
305 272
306 273 return ldap_conn
307 274
308 def get_uid(self, username):
309 uid = username
310 for server_addr in self.SERVER_ADDRESSES:
311 uid = chop_at(username, "@%s" % server_addr)
312 return uid
313
314 275 def fetch_attrs_from_simple_bind(self, server, dn, username, password):
315 276 try:
316 277 log.debug('Trying simple bind with %s', dn)
@@ -336,7 +297,9 b' class AuthLdap(object):'
336 297 :param password: password
337 298 """
338 299
339 uid = self.get_uid(username)
300 uid = self.get_uid(username, self.SERVER_ADDRESSES)
301 user_attrs = {}
302 dn = ''
340 303
341 304 if not password:
342 305 msg = "Authenticating user %s with blank password not allowed"
@@ -350,8 +313,8 b' class AuthLdap(object):'
350 313 ldap_conn = self._get_ldap_conn()
351 314 filter_ = '(&%s(%s=%s))' % (
352 315 self.LDAP_FILTER, self.attr_login, username)
353 log.debug("Authenticating %r filter %s at %s", self.BASE_DN,
354 filter_, self.LDAP_SERVER)
316 log.debug(
317 "Authenticating %r filter %s", self.BASE_DN, filter_)
355 318 lobjects = ldap_conn.search_ext_s(
356 319 self.BASE_DN, self.SEARCH_SCOPE, filter_)
357 320
@@ -57,9 +57,6 b' def trigger_post_push_hook('
57 57 :param repo_alias: the type of SCM repo
58 58 :param commit_ids: list of commit ids that we pushed
59 59 """
60 if repo_alias not in ('hg', 'git'):
61 return
62
63 60 extras = _get_rc_scm_extras(username, repo_name, repo_alias, action)
64 61 extras.commit_ids = commit_ids
65 62 hooks_base.post_push(extras)
General Comments 0
You need to be logged in to leave comments. Login now