##// END OF EJS Templates
templating: use .mako as extensions for template files.
marcink -
r1282:90601d74 default
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,55 +1,53
1 # top level files
1 # top level files
2 include test.ini
2 include test.ini
3 include MANIFEST.in
3 include MANIFEST.in
4 include README.rst
4 include README.rst
5 include CHANGES.rst
5 include CHANGES.rst
6 include LICENSE.txt
6 include LICENSE.txt
7
7
8 include rhodecode/VERSION
8 include rhodecode/VERSION
9
9
10 # docs
10 # docs
11 recursive-include docs *
11 recursive-include docs *
12
12
13 # all config files
13 # all config files
14 recursive-include configs *
14 recursive-include configs *
15
15
16 # translations
16 # translations
17 recursive-include rhodecode/i18n *
17 recursive-include rhodecode/i18n *
18
18
19 # hook templates
19 # hook templates
20 recursive-include rhodecode/config/hook_templates *
20 recursive-include rhodecode/config/hook_templates *
21
21
22 # non-python core stuff
22 # non-python core stuff
23 recursive-include rhodecode *.cfg
23 recursive-include rhodecode *.cfg
24 recursive-include rhodecode *.json
24 recursive-include rhodecode *.json
25 recursive-include rhodecode *.ini_tmpl
25 recursive-include rhodecode *.ini_tmpl
26 recursive-include rhodecode *.sh
26 recursive-include rhodecode *.sh
27 recursive-include rhodecode *.mako
27 recursive-include rhodecode *.mako
28
28
29 # 502 page
29 # 502 page
30 include rhodecode/public/502.html
30 include rhodecode/public/502.html
31
31
32 # 502 page
33 include rhodecode/public/502.html
34
32
35 # images, css
33 # images, css
36 include rhodecode/public/css/*.css
34 include rhodecode/public/css/*.css
37 include rhodecode/public/images/*.*
35 include rhodecode/public/images/*.*
38
36
39 # sound files
37 # sound files
40 include rhodecode/public/sounds/*.mp3
38 include rhodecode/public/sounds/*.mp3
41 include rhodecode/public/sounds/*.wav
39 include rhodecode/public/sounds/*.wav
42
40
43 # fonts
41 # fonts
44 recursive-include rhodecode/public/fonts/ProximaNova *
42 recursive-include rhodecode/public/fonts/ProximaNova *
45 recursive-include rhodecode/public/fonts/RCIcons *
43 recursive-include rhodecode/public/fonts/RCIcons *
46
44
47 # js
45 # js
48 recursive-include rhodecode/public/js *
46 recursive-include rhodecode/public/js *
49
47
50 # templates
48 # templates
51 recursive-include rhodecode/templates *
49 recursive-include rhodecode/templates *
52
50
53 # skip any tests files
51 # skip any tests files
54 recursive-exclude rhodecode/tests *
52 recursive-exclude rhodecode/tests *
55
53
@@ -1,82 +1,82
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import collections
21 import collections
22 import logging
22 import logging
23
23
24 from pylons import tmpl_context as c
24 from pylons import tmpl_context as c
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26
26
27 from rhodecode.lib.auth import (
27 from rhodecode.lib.auth import (
28 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
28 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
29 from rhodecode.lib.utils import read_opensource_licenses
29 from rhodecode.lib.utils import read_opensource_licenses
30 from rhodecode.svn_support.utils import generate_mod_dav_svn_config
30 from rhodecode.svn_support.utils import generate_mod_dav_svn_config
31 from rhodecode.translation import _
31 from rhodecode.translation import _
32
32
33 from .navigation import navigation_list
33 from .navigation import navigation_list
34
34
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 class AdminSettingsView(object):
39 class AdminSettingsView(object):
40
40
41 def __init__(self, context, request):
41 def __init__(self, context, request):
42 self.request = request
42 self.request = request
43 self.context = context
43 self.context = context
44 self.session = request.session
44 self.session = request.session
45 self._rhodecode_user = request.user
45 self._rhodecode_user = request.user
46
46
47 @LoginRequired()
47 @LoginRequired()
48 @HasPermissionAllDecorator('hg.admin')
48 @HasPermissionAllDecorator('hg.admin')
49 @view_config(
49 @view_config(
50 route_name='admin_settings_open_source', request_method='GET',
50 route_name='admin_settings_open_source', request_method='GET',
51 renderer='rhodecode:templates/admin/settings/settings.html')
51 renderer='rhodecode:templates/admin/settings/settings.mako')
52 def open_source_licenses(self):
52 def open_source_licenses(self):
53 c.active = 'open_source'
53 c.active = 'open_source'
54 c.navlist = navigation_list(self.request)
54 c.navlist = navigation_list(self.request)
55 c.opensource_licenses = collections.OrderedDict(
55 c.opensource_licenses = collections.OrderedDict(
56 sorted(read_opensource_licenses().items(), key=lambda t: t[0]))
56 sorted(read_opensource_licenses().items(), key=lambda t: t[0]))
57
57
58 return {}
58 return {}
59
59
60 @LoginRequired()
60 @LoginRequired()
61 @CSRFRequired()
61 @CSRFRequired()
62 @HasPermissionAllDecorator('hg.admin')
62 @HasPermissionAllDecorator('hg.admin')
63 @view_config(
63 @view_config(
64 route_name='admin_settings_vcs_svn_generate_cfg',
64 route_name='admin_settings_vcs_svn_generate_cfg',
65 request_method='POST', renderer='json')
65 request_method='POST', renderer='json')
66 def vcs_svn_generate_config(self):
66 def vcs_svn_generate_config(self):
67 try:
67 try:
68 generate_mod_dav_svn_config(self.request.registry)
68 generate_mod_dav_svn_config(self.request.registry)
69 msg = {
69 msg = {
70 'message': _('Apache configuration for Subversion generated.'),
70 'message': _('Apache configuration for Subversion generated.'),
71 'level': 'success',
71 'level': 'success',
72 }
72 }
73 except Exception:
73 except Exception:
74 log.exception(
74 log.exception(
75 'Exception while generating the Apache configuration for Subversion.')
75 'Exception while generating the Apache configuration for Subversion.')
76 msg = {
76 msg = {
77 'message': _('Failed to generate the Apache configuration for Subversion.'),
77 'message': _('Failed to generate the Apache configuration for Subversion.'),
78 'level': 'error',
78 'level': 'error',
79 }
79 }
80
80
81 data = {'message': msg}
81 data = {'message': msg}
82 return data
82 return data
@@ -1,284 +1,284
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2017 RhodeCode GmbH
3 # Copyright (C) 2012-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 RhodeCode authentication plugin for Atlassian CROWD
22 RhodeCode authentication plugin for Atlassian CROWD
23 """
23 """
24
24
25
25
26 import colander
26 import colander
27 import base64
27 import base64
28 import logging
28 import logging
29 import urllib2
29 import urllib2
30
30
31 from pylons.i18n.translation import lazy_ugettext as _
31 from pylons.i18n.translation import lazy_ugettext as _
32 from sqlalchemy.ext.hybrid import hybrid_property
32 from sqlalchemy.ext.hybrid import hybrid_property
33
33
34 from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin
34 from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin
35 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
35 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
36 from rhodecode.authentication.routes import AuthnPluginResourceBase
36 from rhodecode.authentication.routes import AuthnPluginResourceBase
37 from rhodecode.lib.colander_utils import strip_whitespace
37 from rhodecode.lib.colander_utils import strip_whitespace
38 from rhodecode.lib.ext_json import json, formatted_json
38 from rhodecode.lib.ext_json import json, formatted_json
39 from rhodecode.model.db import User
39 from rhodecode.model.db import User
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43
43
44 def plugin_factory(plugin_id, *args, **kwds):
44 def plugin_factory(plugin_id, *args, **kwds):
45 """
45 """
46 Factory function that is called during plugin discovery.
46 Factory function that is called during plugin discovery.
47 It returns the plugin instance.
47 It returns the plugin instance.
48 """
48 """
49 plugin = RhodeCodeAuthPlugin(plugin_id)
49 plugin = RhodeCodeAuthPlugin(plugin_id)
50 return plugin
50 return plugin
51
51
52
52
53 class CrowdAuthnResource(AuthnPluginResourceBase):
53 class CrowdAuthnResource(AuthnPluginResourceBase):
54 pass
54 pass
55
55
56
56
57 class CrowdSettingsSchema(AuthnPluginSettingsSchemaBase):
57 class CrowdSettingsSchema(AuthnPluginSettingsSchemaBase):
58 host = colander.SchemaNode(
58 host = colander.SchemaNode(
59 colander.String(),
59 colander.String(),
60 default='127.0.0.1',
60 default='127.0.0.1',
61 description=_('The FQDN or IP of the Atlassian CROWD Server'),
61 description=_('The FQDN or IP of the Atlassian CROWD Server'),
62 preparer=strip_whitespace,
62 preparer=strip_whitespace,
63 title=_('Host'),
63 title=_('Host'),
64 widget='string')
64 widget='string')
65 port = colander.SchemaNode(
65 port = colander.SchemaNode(
66 colander.Int(),
66 colander.Int(),
67 default=8095,
67 default=8095,
68 description=_('The Port in use by the Atlassian CROWD Server'),
68 description=_('The Port in use by the Atlassian CROWD Server'),
69 preparer=strip_whitespace,
69 preparer=strip_whitespace,
70 title=_('Port'),
70 title=_('Port'),
71 validator=colander.Range(min=0, max=65536),
71 validator=colander.Range(min=0, max=65536),
72 widget='int')
72 widget='int')
73 app_name = colander.SchemaNode(
73 app_name = colander.SchemaNode(
74 colander.String(),
74 colander.String(),
75 default='',
75 default='',
76 description=_('The Application Name to authenticate to CROWD'),
76 description=_('The Application Name to authenticate to CROWD'),
77 preparer=strip_whitespace,
77 preparer=strip_whitespace,
78 title=_('Application Name'),
78 title=_('Application Name'),
79 widget='string')
79 widget='string')
80 app_password = colander.SchemaNode(
80 app_password = colander.SchemaNode(
81 colander.String(),
81 colander.String(),
82 default='',
82 default='',
83 description=_('The password to authenticate to CROWD'),
83 description=_('The password to authenticate to CROWD'),
84 preparer=strip_whitespace,
84 preparer=strip_whitespace,
85 title=_('Application Password'),
85 title=_('Application Password'),
86 widget='password')
86 widget='password')
87 admin_groups = colander.SchemaNode(
87 admin_groups = colander.SchemaNode(
88 colander.String(),
88 colander.String(),
89 default='',
89 default='',
90 description=_('A comma separated list of group names that identify '
90 description=_('A comma separated list of group names that identify '
91 'users as RhodeCode Administrators'),
91 'users as RhodeCode Administrators'),
92 missing='',
92 missing='',
93 preparer=strip_whitespace,
93 preparer=strip_whitespace,
94 title=_('Admin Groups'),
94 title=_('Admin Groups'),
95 widget='string')
95 widget='string')
96
96
97
97
98 class CrowdServer(object):
98 class CrowdServer(object):
99 def __init__(self, *args, **kwargs):
99 def __init__(self, *args, **kwargs):
100 """
100 """
101 Create a new CrowdServer object that points to IP/Address 'host',
101 Create a new CrowdServer object that points to IP/Address 'host',
102 on the given port, and using the given method (https/http). user and
102 on the given port, and using the given method (https/http). user and
103 passwd can be set here or with set_credentials. If unspecified,
103 passwd can be set here or with set_credentials. If unspecified,
104 "version" defaults to "latest".
104 "version" defaults to "latest".
105
105
106 example::
106 example::
107
107
108 cserver = CrowdServer(host="127.0.0.1",
108 cserver = CrowdServer(host="127.0.0.1",
109 port="8095",
109 port="8095",
110 user="some_app",
110 user="some_app",
111 passwd="some_passwd",
111 passwd="some_passwd",
112 version="1")
112 version="1")
113 """
113 """
114 if not "port" in kwargs:
114 if not "port" in kwargs:
115 kwargs["port"] = "8095"
115 kwargs["port"] = "8095"
116 self._logger = kwargs.get("logger", logging.getLogger(__name__))
116 self._logger = kwargs.get("logger", logging.getLogger(__name__))
117 self._uri = "%s://%s:%s/crowd" % (kwargs.get("method", "http"),
117 self._uri = "%s://%s:%s/crowd" % (kwargs.get("method", "http"),
118 kwargs.get("host", "127.0.0.1"),
118 kwargs.get("host", "127.0.0.1"),
119 kwargs.get("port", "8095"))
119 kwargs.get("port", "8095"))
120 self.set_credentials(kwargs.get("user", ""),
120 self.set_credentials(kwargs.get("user", ""),
121 kwargs.get("passwd", ""))
121 kwargs.get("passwd", ""))
122 self._version = kwargs.get("version", "latest")
122 self._version = kwargs.get("version", "latest")
123 self._url_list = None
123 self._url_list = None
124 self._appname = "crowd"
124 self._appname = "crowd"
125
125
126 def set_credentials(self, user, passwd):
126 def set_credentials(self, user, passwd):
127 self.user = user
127 self.user = user
128 self.passwd = passwd
128 self.passwd = passwd
129 self._make_opener()
129 self._make_opener()
130
130
131 def _make_opener(self):
131 def _make_opener(self):
132 mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
132 mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
133 mgr.add_password(None, self._uri, self.user, self.passwd)
133 mgr.add_password(None, self._uri, self.user, self.passwd)
134 handler = urllib2.HTTPBasicAuthHandler(mgr)
134 handler = urllib2.HTTPBasicAuthHandler(mgr)
135 self.opener = urllib2.build_opener(handler)
135 self.opener = urllib2.build_opener(handler)
136
136
137 def _request(self, url, body=None, headers=None,
137 def _request(self, url, body=None, headers=None,
138 method=None, noformat=False,
138 method=None, noformat=False,
139 empty_response_ok=False):
139 empty_response_ok=False):
140 _headers = {"Content-type": "application/json",
140 _headers = {"Content-type": "application/json",
141 "Accept": "application/json"}
141 "Accept": "application/json"}
142 if self.user and self.passwd:
142 if self.user and self.passwd:
143 authstring = base64.b64encode("%s:%s" % (self.user, self.passwd))
143 authstring = base64.b64encode("%s:%s" % (self.user, self.passwd))
144 _headers["Authorization"] = "Basic %s" % authstring
144 _headers["Authorization"] = "Basic %s" % authstring
145 if headers:
145 if headers:
146 _headers.update(headers)
146 _headers.update(headers)
147 log.debug("Sent crowd: \n%s"
147 log.debug("Sent crowd: \n%s"
148 % (formatted_json({"url": url, "body": body,
148 % (formatted_json({"url": url, "body": body,
149 "headers": _headers})))
149 "headers": _headers})))
150 request = urllib2.Request(url, body, _headers)
150 request = urllib2.Request(url, body, _headers)
151 if method:
151 if method:
152 request.get_method = lambda: method
152 request.get_method = lambda: method
153
153
154 global msg
154 global msg
155 msg = ""
155 msg = ""
156 try:
156 try:
157 rdoc = self.opener.open(request)
157 rdoc = self.opener.open(request)
158 msg = "".join(rdoc.readlines())
158 msg = "".join(rdoc.readlines())
159 if not msg and empty_response_ok:
159 if not msg and empty_response_ok:
160 rval = {}
160 rval = {}
161 rval["status"] = True
161 rval["status"] = True
162 rval["error"] = "Response body was empty"
162 rval["error"] = "Response body was empty"
163 elif not noformat:
163 elif not noformat:
164 rval = json.loads(msg)
164 rval = json.loads(msg)
165 rval["status"] = True
165 rval["status"] = True
166 else:
166 else:
167 rval = "".join(rdoc.readlines())
167 rval = "".join(rdoc.readlines())
168 except Exception as e:
168 except Exception as e:
169 if not noformat:
169 if not noformat:
170 rval = {"status": False,
170 rval = {"status": False,
171 "body": body,
171 "body": body,
172 "error": str(e) + "\n" + msg}
172 "error": str(e) + "\n" + msg}
173 else:
173 else:
174 rval = None
174 rval = None
175 return rval
175 return rval
176
176
177 def user_auth(self, username, password):
177 def user_auth(self, username, password):
178 """Authenticate a user against crowd. Returns brief information about
178 """Authenticate a user against crowd. Returns brief information about
179 the user."""
179 the user."""
180 url = ("%s/rest/usermanagement/%s/authentication?username=%s"
180 url = ("%s/rest/usermanagement/%s/authentication?username=%s"
181 % (self._uri, self._version, username))
181 % (self._uri, self._version, username))
182 body = json.dumps({"value": password})
182 body = json.dumps({"value": password})
183 return self._request(url, body)
183 return self._request(url, body)
184
184
185 def user_groups(self, username):
185 def user_groups(self, username):
186 """Retrieve a list of groups to which this user belongs."""
186 """Retrieve a list of groups to which this user belongs."""
187 url = ("%s/rest/usermanagement/%s/user/group/nested?username=%s"
187 url = ("%s/rest/usermanagement/%s/user/group/nested?username=%s"
188 % (self._uri, self._version, username))
188 % (self._uri, self._version, username))
189 return self._request(url)
189 return self._request(url)
190
190
191
191
192 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
192 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
193
193
194 def includeme(self, config):
194 def includeme(self, config):
195 config.add_authn_plugin(self)
195 config.add_authn_plugin(self)
196 config.add_authn_resource(self.get_id(), CrowdAuthnResource(self))
196 config.add_authn_resource(self.get_id(), CrowdAuthnResource(self))
197 config.add_view(
197 config.add_view(
198 'rhodecode.authentication.views.AuthnPluginViewBase',
198 'rhodecode.authentication.views.AuthnPluginViewBase',
199 attr='settings_get',
199 attr='settings_get',
200 renderer='rhodecode:templates/admin/auth/plugin_settings.html',
200 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
201 request_method='GET',
201 request_method='GET',
202 route_name='auth_home',
202 route_name='auth_home',
203 context=CrowdAuthnResource)
203 context=CrowdAuthnResource)
204 config.add_view(
204 config.add_view(
205 'rhodecode.authentication.views.AuthnPluginViewBase',
205 'rhodecode.authentication.views.AuthnPluginViewBase',
206 attr='settings_post',
206 attr='settings_post',
207 renderer='rhodecode:templates/admin/auth/plugin_settings.html',
207 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
208 request_method='POST',
208 request_method='POST',
209 route_name='auth_home',
209 route_name='auth_home',
210 context=CrowdAuthnResource)
210 context=CrowdAuthnResource)
211
211
212 def get_settings_schema(self):
212 def get_settings_schema(self):
213 return CrowdSettingsSchema()
213 return CrowdSettingsSchema()
214
214
215 def get_display_name(self):
215 def get_display_name(self):
216 return _('CROWD')
216 return _('CROWD')
217
217
218 @hybrid_property
218 @hybrid_property
219 def name(self):
219 def name(self):
220 return "crowd"
220 return "crowd"
221
221
222 def use_fake_password(self):
222 def use_fake_password(self):
223 return True
223 return True
224
224
225 def user_activation_state(self):
225 def user_activation_state(self):
226 def_user_perms = User.get_default_user().AuthUser.permissions['global']
226 def_user_perms = User.get_default_user().AuthUser.permissions['global']
227 return 'hg.extern_activate.auto' in def_user_perms
227 return 'hg.extern_activate.auto' in def_user_perms
228
228
229 def auth(self, userobj, username, password, settings, **kwargs):
229 def auth(self, userobj, username, password, settings, **kwargs):
230 """
230 """
231 Given a user object (which may be null), username, a plaintext password,
231 Given a user object (which may be null), username, a plaintext password,
232 and a settings object (containing all the keys needed as listed in settings()),
232 and a settings object (containing all the keys needed as listed in settings()),
233 authenticate this user's login attempt.
233 authenticate this user's login attempt.
234
234
235 Return None on failure. On success, return a dictionary of the form:
235 Return None on failure. On success, return a dictionary of the form:
236
236
237 see: RhodeCodeAuthPluginBase.auth_func_attrs
237 see: RhodeCodeAuthPluginBase.auth_func_attrs
238 This is later validated for correctness
238 This is later validated for correctness
239 """
239 """
240 if not username or not password:
240 if not username or not password:
241 log.debug('Empty username or password skipping...')
241 log.debug('Empty username or password skipping...')
242 return None
242 return None
243
243
244 log.debug("Crowd settings: \n%s" % (formatted_json(settings)))
244 log.debug("Crowd settings: \n%s" % (formatted_json(settings)))
245 server = CrowdServer(**settings)
245 server = CrowdServer(**settings)
246 server.set_credentials(settings["app_name"], settings["app_password"])
246 server.set_credentials(settings["app_name"], settings["app_password"])
247 crowd_user = server.user_auth(username, password)
247 crowd_user = server.user_auth(username, password)
248 log.debug("Crowd returned: \n%s" % (formatted_json(crowd_user)))
248 log.debug("Crowd returned: \n%s" % (formatted_json(crowd_user)))
249 if not crowd_user["status"]:
249 if not crowd_user["status"]:
250 return None
250 return None
251
251
252 res = server.user_groups(crowd_user["name"])
252 res = server.user_groups(crowd_user["name"])
253 log.debug("Crowd groups: \n%s" % (formatted_json(res)))
253 log.debug("Crowd groups: \n%s" % (formatted_json(res)))
254 crowd_user["groups"] = [x["name"] for x in res["groups"]]
254 crowd_user["groups"] = [x["name"] for x in res["groups"]]
255
255
256 # old attrs fetched from RhodeCode database
256 # old attrs fetched from RhodeCode database
257 admin = getattr(userobj, 'admin', False)
257 admin = getattr(userobj, 'admin', False)
258 active = getattr(userobj, 'active', True)
258 active = getattr(userobj, 'active', True)
259 email = getattr(userobj, 'email', '')
259 email = getattr(userobj, 'email', '')
260 username = getattr(userobj, 'username', username)
260 username = getattr(userobj, 'username', username)
261 firstname = getattr(userobj, 'firstname', '')
261 firstname = getattr(userobj, 'firstname', '')
262 lastname = getattr(userobj, 'lastname', '')
262 lastname = getattr(userobj, 'lastname', '')
263 extern_type = getattr(userobj, 'extern_type', '')
263 extern_type = getattr(userobj, 'extern_type', '')
264
264
265 user_attrs = {
265 user_attrs = {
266 'username': username,
266 'username': username,
267 'firstname': crowd_user["first-name"] or firstname,
267 'firstname': crowd_user["first-name"] or firstname,
268 'lastname': crowd_user["last-name"] or lastname,
268 'lastname': crowd_user["last-name"] or lastname,
269 'groups': crowd_user["groups"],
269 'groups': crowd_user["groups"],
270 'email': crowd_user["email"] or email,
270 'email': crowd_user["email"] or email,
271 'admin': admin,
271 'admin': admin,
272 'active': active,
272 'active': active,
273 'active_from_extern': crowd_user.get('active'),
273 'active_from_extern': crowd_user.get('active'),
274 'extern_name': crowd_user["name"],
274 'extern_name': crowd_user["name"],
275 'extern_type': extern_type,
275 'extern_type': extern_type,
276 }
276 }
277
277
278 # set an admin if we're in admin_groups of crowd
278 # set an admin if we're in admin_groups of crowd
279 for group in settings["admin_groups"]:
279 for group in settings["admin_groups"]:
280 if group in user_attrs["groups"]:
280 if group in user_attrs["groups"]:
281 user_attrs["admin"] = True
281 user_attrs["admin"] = True
282 log.debug("Final crowd user object: \n%s" % (formatted_json(user_attrs)))
282 log.debug("Final crowd user object: \n%s" % (formatted_json(user_attrs)))
283 log.info('user %s authenticated correctly' % user_attrs['username'])
283 log.info('user %s authenticated correctly' % user_attrs['username'])
284 return user_attrs
284 return user_attrs
@@ -1,225 +1,225
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2017 RhodeCode GmbH
3 # Copyright (C) 2012-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import colander
21 import colander
22 import logging
22 import logging
23
23
24 from sqlalchemy.ext.hybrid import hybrid_property
24 from sqlalchemy.ext.hybrid import hybrid_property
25
25
26 from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin
26 from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin
27 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
27 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
28 from rhodecode.authentication.routes import AuthnPluginResourceBase
28 from rhodecode.authentication.routes import AuthnPluginResourceBase
29 from rhodecode.lib.colander_utils import strip_whitespace
29 from rhodecode.lib.colander_utils import strip_whitespace
30 from rhodecode.lib.utils2 import str2bool, safe_unicode
30 from rhodecode.lib.utils2 import str2bool, safe_unicode
31 from rhodecode.model.db import User
31 from rhodecode.model.db import User
32 from rhodecode.translation import _
32 from rhodecode.translation import _
33
33
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37
37
38 def plugin_factory(plugin_id, *args, **kwds):
38 def plugin_factory(plugin_id, *args, **kwds):
39 """
39 """
40 Factory function that is called during plugin discovery.
40 Factory function that is called during plugin discovery.
41 It returns the plugin instance.
41 It returns the plugin instance.
42 """
42 """
43 plugin = RhodeCodeAuthPlugin(plugin_id)
43 plugin = RhodeCodeAuthPlugin(plugin_id)
44 return plugin
44 return plugin
45
45
46
46
47 class HeadersAuthnResource(AuthnPluginResourceBase):
47 class HeadersAuthnResource(AuthnPluginResourceBase):
48 pass
48 pass
49
49
50
50
51 class HeadersSettingsSchema(AuthnPluginSettingsSchemaBase):
51 class HeadersSettingsSchema(AuthnPluginSettingsSchemaBase):
52 header = colander.SchemaNode(
52 header = colander.SchemaNode(
53 colander.String(),
53 colander.String(),
54 default='REMOTE_USER',
54 default='REMOTE_USER',
55 description=_('Header to extract the user from'),
55 description=_('Header to extract the user from'),
56 preparer=strip_whitespace,
56 preparer=strip_whitespace,
57 title=_('Header'),
57 title=_('Header'),
58 widget='string')
58 widget='string')
59 fallback_header = colander.SchemaNode(
59 fallback_header = colander.SchemaNode(
60 colander.String(),
60 colander.String(),
61 default='HTTP_X_FORWARDED_USER',
61 default='HTTP_X_FORWARDED_USER',
62 description=_('Header to extract the user from when main one fails'),
62 description=_('Header to extract the user from when main one fails'),
63 preparer=strip_whitespace,
63 preparer=strip_whitespace,
64 title=_('Fallback header'),
64 title=_('Fallback header'),
65 widget='string')
65 widget='string')
66 clean_username = colander.SchemaNode(
66 clean_username = colander.SchemaNode(
67 colander.Boolean(),
67 colander.Boolean(),
68 default=True,
68 default=True,
69 description=_('Perform cleaning of user, if passed user has @ in '
69 description=_('Perform cleaning of user, if passed user has @ in '
70 'username then first part before @ is taken. '
70 'username then first part before @ is taken. '
71 'If there\'s \\ in the username only the part after '
71 'If there\'s \\ in the username only the part after '
72 ' \\ is taken'),
72 ' \\ is taken'),
73 missing=False,
73 missing=False,
74 title=_('Clean username'),
74 title=_('Clean username'),
75 widget='bool')
75 widget='bool')
76
76
77
77
78 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
78 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
79
79
80 def includeme(self, config):
80 def includeme(self, config):
81 config.add_authn_plugin(self)
81 config.add_authn_plugin(self)
82 config.add_authn_resource(self.get_id(), HeadersAuthnResource(self))
82 config.add_authn_resource(self.get_id(), HeadersAuthnResource(self))
83 config.add_view(
83 config.add_view(
84 'rhodecode.authentication.views.AuthnPluginViewBase',
84 'rhodecode.authentication.views.AuthnPluginViewBase',
85 attr='settings_get',
85 attr='settings_get',
86 renderer='rhodecode:templates/admin/auth/plugin_settings.html',
86 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
87 request_method='GET',
87 request_method='GET',
88 route_name='auth_home',
88 route_name='auth_home',
89 context=HeadersAuthnResource)
89 context=HeadersAuthnResource)
90 config.add_view(
90 config.add_view(
91 'rhodecode.authentication.views.AuthnPluginViewBase',
91 'rhodecode.authentication.views.AuthnPluginViewBase',
92 attr='settings_post',
92 attr='settings_post',
93 renderer='rhodecode:templates/admin/auth/plugin_settings.html',
93 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
94 request_method='POST',
94 request_method='POST',
95 route_name='auth_home',
95 route_name='auth_home',
96 context=HeadersAuthnResource)
96 context=HeadersAuthnResource)
97
97
98 def get_display_name(self):
98 def get_display_name(self):
99 return _('Headers')
99 return _('Headers')
100
100
101 def get_settings_schema(self):
101 def get_settings_schema(self):
102 return HeadersSettingsSchema()
102 return HeadersSettingsSchema()
103
103
104 @hybrid_property
104 @hybrid_property
105 def name(self):
105 def name(self):
106 return 'headers'
106 return 'headers'
107
107
108 @property
108 @property
109 def is_headers_auth(self):
109 def is_headers_auth(self):
110 return True
110 return True
111
111
112 def use_fake_password(self):
112 def use_fake_password(self):
113 return True
113 return True
114
114
115 def user_activation_state(self):
115 def user_activation_state(self):
116 def_user_perms = User.get_default_user().AuthUser.permissions['global']
116 def_user_perms = User.get_default_user().AuthUser.permissions['global']
117 return 'hg.extern_activate.auto' in def_user_perms
117 return 'hg.extern_activate.auto' in def_user_perms
118
118
119 def _clean_username(self, username):
119 def _clean_username(self, username):
120 # Removing realm and domain from username
120 # Removing realm and domain from username
121 username = username.split('@')[0]
121 username = username.split('@')[0]
122 username = username.rsplit('\\')[-1]
122 username = username.rsplit('\\')[-1]
123 return username
123 return username
124
124
125 def _get_username(self, environ, settings):
125 def _get_username(self, environ, settings):
126 username = None
126 username = None
127 environ = environ or {}
127 environ = environ or {}
128 if not environ:
128 if not environ:
129 log.debug('got empty environ: %s' % environ)