Show More
@@ -627,3 +627,21 b' def authenticate(username, password, env' | |||||
627 | log.debug("User `%s` failed to authenticate against %s", |
|
627 | log.debug("User `%s` failed to authenticate against %s", | |
628 | display_user, plugin.get_id()) |
|
628 | display_user, plugin.get_id()) | |
629 | return None |
|
629 | return None | |
|
630 | ||||
|
631 | ||||
|
632 | def chop_at(s, sub, inclusive=False): | |||
|
633 | """Truncate string ``s`` at the first occurrence of ``sub``. | |||
|
634 | ||||
|
635 | If ``inclusive`` is true, truncate just after ``sub`` rather than at it. | |||
|
636 | ||||
|
637 | >>> chop_at("plutocratic brats", "rat") | |||
|
638 | 'plutoc' | |||
|
639 | >>> chop_at("plutocratic brats", "rat", True) | |||
|
640 | 'plutocrat' | |||
|
641 | """ | |||
|
642 | pos = s.find(sub) | |||
|
643 | if pos == -1: | |||
|
644 | return s | |||
|
645 | if inclusive: | |||
|
646 | return s[:pos+len(sub)] | |||
|
647 | return s[:pos] |
@@ -28,10 +28,9 b' import base64' | |||||
28 | import logging |
|
28 | import logging | |
29 | import urllib2 |
|
29 | import urllib2 | |
30 |
|
30 | |||
31 |
from |
|
31 | from rhodecode.translation import _ | |
32 | from sqlalchemy.ext.hybrid import hybrid_property |
|
32 | from rhodecode.authentication.base import ( | |
33 |
|
33 | RhodeCodeExternalAuthPlugin, hybrid_property) | ||
34 | from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin |
|
|||
35 | from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase |
|
34 | from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase | |
36 | from rhodecode.authentication.routes import AuthnPluginResourceBase |
|
35 | from rhodecode.authentication.routes import AuthnPluginResourceBase | |
37 | from rhodecode.lib.colander_utils import strip_whitespace |
|
36 | from rhodecode.lib.colander_utils import strip_whitespace |
@@ -21,15 +21,14 b'' | |||||
21 | import colander |
|
21 | import colander | |
22 | import logging |
|
22 | import logging | |
23 |
|
23 | |||
24 | from sqlalchemy.ext.hybrid import hybrid_property |
|
24 | from rhodecode.translation import _ | |
25 |
|
25 | from rhodecode.authentication.base import ( | ||
26 | from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin |
|
26 | RhodeCodeExternalAuthPlugin, hybrid_property) | |
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 _ |
|
|||
33 |
|
32 | |||
34 |
|
33 | |||
35 | log = logging.getLogger(__name__) |
|
34 | log = logging.getLogger(__name__) |
@@ -30,10 +30,9 b' import rhodecode' | |||||
30 | import urllib |
|
30 | import urllib | |
31 | import urllib2 |
|
31 | import urllib2 | |
32 |
|
32 | |||
33 |
from |
|
33 | from rhodecode.translation import _ | |
34 | from sqlalchemy.ext.hybrid import hybrid_property |
|
34 | from rhodecode.authentication.base import ( | |
35 |
|
35 | RhodeCodeExternalAuthPlugin, hybrid_property) | ||
36 | from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin |
|
|||
37 | from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase |
|
36 | from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase | |
38 | from rhodecode.authentication.routes import AuthnPluginResourceBase |
|
37 | from rhodecode.authentication.routes import AuthnPluginResourceBase | |
39 | from rhodecode.lib.colander_utils import strip_whitespace |
|
38 | from rhodecode.lib.colander_utils import strip_whitespace |
@@ -27,10 +27,9 b' import colander' | |||||
27 | import logging |
|
27 | import logging | |
28 | import traceback |
|
28 | import traceback | |
29 |
|
29 | |||
30 |
from |
|
30 | from rhodecode.translation import _ | |
31 | from sqlalchemy.ext.hybrid import hybrid_property |
|
31 | from rhodecode.authentication.base import ( | |
32 |
|
32 | RhodeCodeExternalAuthPlugin, chop_at, hybrid_property) | ||
33 | from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin |
|
|||
34 | from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase |
|
33 | from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase | |
35 | from rhodecode.authentication.routes import AuthnPluginResourceBase |
|
34 | from rhodecode.authentication.routes import AuthnPluginResourceBase | |
36 | from rhodecode.lib.colander_utils import strip_whitespace |
|
35 | from rhodecode.lib.colander_utils import strip_whitespace | |
@@ -72,14 +71,15 b' class LdapSettingsSchema(AuthnPluginSett' | |||||
72 | host = colander.SchemaNode( |
|
71 | host = colander.SchemaNode( | |
73 | colander.String(), |
|
72 | colander.String(), | |
74 | default='', |
|
73 | default='', | |
75 |
description=_('Host of the LDAP Server' |
|
74 | description=_('Host of the LDAP Server \n' | |
|
75 | '(e.g., 192.168.2.154, or ldap-server.domain.com'), | |||
76 | preparer=strip_whitespace, |
|
76 | preparer=strip_whitespace, | |
77 | title=_('LDAP Host'), |
|
77 | title=_('LDAP Host'), | |
78 | widget='string') |
|
78 | widget='string') | |
79 | port = colander.SchemaNode( |
|
79 | port = colander.SchemaNode( | |
80 | colander.Int(), |
|
80 | colander.Int(), | |
81 | default=389, |
|
81 | default=389, | |
82 |
description=_(' |
|
82 | description=_('Custom port that the LDAP server is listening on. Default: 389'), | |
83 | preparer=strip_whitespace, |
|
83 | preparer=strip_whitespace, | |
84 | title=_('Port'), |
|
84 | title=_('Port'), | |
85 | validator=colander.Range(min=0, max=65536), |
|
85 | validator=colander.Range(min=0, max=65536), | |
@@ -87,7 +87,9 b' class LdapSettingsSchema(AuthnPluginSett' | |||||
87 | dn_user = colander.SchemaNode( |
|
87 | dn_user = colander.SchemaNode( | |
88 | colander.String(), |
|
88 | colander.String(), | |
89 | default='', |
|
89 | default='', | |
90 |
description=_(' |
|
90 | description=_('Optional user DN/account to connect to LDAP if authentication is required. \n' | |
|
91 | 'e.g., cn=admin,dc=mydomain,dc=com, or ' | |||
|
92 | 'uid=root,cn=users,dc=mydomain,dc=com, or admin@mydomain.com'), | |||
91 | missing='', |
|
93 | missing='', | |
92 | preparer=strip_whitespace, |
|
94 | preparer=strip_whitespace, | |
93 | title=_('Account'), |
|
95 | title=_('Account'), | |
@@ -95,7 +97,7 b' class LdapSettingsSchema(AuthnPluginSett' | |||||
95 | dn_pass = colander.SchemaNode( |
|
97 | dn_pass = colander.SchemaNode( | |
96 | colander.String(), |
|
98 | colander.String(), | |
97 | default='', |
|
99 | default='', | |
98 |
description=_('Password to |
|
100 | description=_('Password to authenticate for given user DN.'), | |
99 | missing='', |
|
101 | missing='', | |
100 | preparer=strip_whitespace, |
|
102 | preparer=strip_whitespace, | |
101 | title=_('Password'), |
|
103 | title=_('Password'), | |
@@ -117,7 +119,9 b' class LdapSettingsSchema(AuthnPluginSett' | |||||
117 | base_dn = colander.SchemaNode( |
|
119 | base_dn = colander.SchemaNode( | |
118 | colander.String(), |
|
120 | colander.String(), | |
119 | default='', |
|
121 | default='', | |
120 |
description=_('Base DN to search |
|
122 | description=_('Base DN to search. Dynamic bind is supported. Add `$login` marker ' | |
|
123 | 'in it to be replaced with current user credentials \n' | |||
|
124 | '(e.g., dc=mydomain,dc=com, or ou=Users,dc=mydomain,dc=com)'), | |||
121 | missing='', |
|
125 | missing='', | |
122 | preparer=strip_whitespace, |
|
126 | preparer=strip_whitespace, | |
123 | title=_('Base DN'), |
|
127 | title=_('Base DN'), | |
@@ -125,22 +129,25 b' class LdapSettingsSchema(AuthnPluginSett' | |||||
125 | filter = colander.SchemaNode( |
|
129 | filter = colander.SchemaNode( | |
126 | colander.String(), |
|
130 | colander.String(), | |
127 | default='', |
|
131 | default='', | |
128 |
description=_('Filter to narrow results |
|
132 | description=_('Filter to narrow results \n' | |
|
133 | '(e.g., (&(objectCategory=Person)(objectClass=user)), or \n' | |||
|
134 | '(memberof=cn=rc-login,ou=groups,ou=company,dc=mydomain,dc=com)))'), | |||
129 | missing='', |
|
135 | missing='', | |
130 | preparer=strip_whitespace, |
|
136 | preparer=strip_whitespace, | |
131 | title=_('LDAP Search Filter'), |
|
137 | title=_('LDAP Search Filter'), | |
132 | widget='string') |
|
138 | widget='string') | |
|
139 | ||||
133 | search_scope = colander.SchemaNode( |
|
140 | search_scope = colander.SchemaNode( | |
134 | colander.String(), |
|
141 | colander.String(), | |
135 |
default=search_scope_choices[ |
|
142 | default=search_scope_choices[2], | |
136 | description=_('How deep to search LDAP'), |
|
143 | description=_('How deep to search LDAP. If unsure set to SUBTREE'), | |
137 | title=_('LDAP Search Scope'), |
|
144 | title=_('LDAP Search Scope'), | |
138 | validator=colander.OneOf(search_scope_choices), |
|
145 | validator=colander.OneOf(search_scope_choices), | |
139 | widget='select') |
|
146 | widget='select') | |
140 | attr_login = colander.SchemaNode( |
|
147 | attr_login = colander.SchemaNode( | |
141 | colander.String(), |
|
148 | colander.String(), | |
142 | default='', |
|
149 | default='uid', | |
143 | description=_('LDAP Attribute to map to user name'), |
|
150 | description=_('LDAP Attribute to map to user name (e.g., uid, or sAMAccountName)'), | |
144 | preparer=strip_whitespace, |
|
151 | preparer=strip_whitespace, | |
145 | title=_('Login Attribute'), |
|
152 | title=_('Login Attribute'), | |
146 | missing_msg=_('The LDAP Login attribute of the CN must be specified'), |
|
153 | missing_msg=_('The LDAP Login attribute of the CN must be specified'), | |
@@ -148,7 +155,7 b' class LdapSettingsSchema(AuthnPluginSett' | |||||
148 | attr_firstname = colander.SchemaNode( |
|
155 | attr_firstname = colander.SchemaNode( | |
149 | colander.String(), |
|
156 | colander.String(), | |
150 | default='', |
|
157 | default='', | |
151 | description=_('LDAP Attribute to map to first name'), |
|
158 | description=_('LDAP Attribute to map to first name (e.g., givenName)'), | |
152 | missing='', |
|
159 | missing='', | |
153 | preparer=strip_whitespace, |
|
160 | preparer=strip_whitespace, | |
154 | title=_('First Name Attribute'), |
|
161 | title=_('First Name Attribute'), | |
@@ -156,7 +163,7 b' class LdapSettingsSchema(AuthnPluginSett' | |||||
156 | attr_lastname = colander.SchemaNode( |
|
163 | attr_lastname = colander.SchemaNode( | |
157 | colander.String(), |
|
164 | colander.String(), | |
158 | default='', |
|
165 | default='', | |
159 | description=_('LDAP Attribute to map to last name'), |
|
166 | description=_('LDAP Attribute to map to last name (e.g., sn)'), | |
160 | missing='', |
|
167 | missing='', | |
161 | preparer=strip_whitespace, |
|
168 | preparer=strip_whitespace, | |
162 | title=_('Last Name Attribute'), |
|
169 | title=_('Last Name Attribute'), | |
@@ -164,7 +171,9 b' class LdapSettingsSchema(AuthnPluginSett' | |||||
164 | attr_email = colander.SchemaNode( |
|
171 | attr_email = colander.SchemaNode( | |
165 | colander.String(), |
|
172 | colander.String(), | |
166 | default='', |
|
173 | default='', | |
167 |
description=_('LDAP Attribute to map to email address' |
|
174 | description=_('LDAP Attribute to map to email address (e.g., mail).\n' | |
|
175 | 'Emails are a crucial part of RhodeCode. \n' | |||
|
176 | 'If possible add a valid email attribute to ldap users.'), | |||
168 | missing='', |
|
177 | missing='', | |
169 | preparer=strip_whitespace, |
|
178 | preparer=strip_whitespace, | |
170 | title=_('Email Attribute'), |
|
179 | title=_('Email Attribute'), | |
@@ -182,7 +191,7 b' class AuthLdap(object):' | |||||
182 | def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='', |
|
191 | def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='', | |
183 | tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3, |
|
192 | tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3, | |
184 | search_scope='SUBTREE', attr_login='uid', |
|
193 | search_scope='SUBTREE', attr_login='uid', | |
185 | ldap_filter='(&(objectClass=user)(!(objectClass=computer)))'): |
|
194 | ldap_filter=None): | |
186 | if ldap == Missing: |
|
195 | if ldap == Missing: | |
187 | raise LdapImportError("Missing or incompatible ldap library") |
|
196 | raise LdapImportError("Missing or incompatible ldap library") | |
188 |
|
197 | |||
@@ -236,14 +245,13 b' class AuthLdap(object):' | |||||
236 | server.start_tls_s() |
|
245 | server.start_tls_s() | |
237 |
|
246 | |||
238 | if self.LDAP_BIND_DN and self.LDAP_BIND_PASS: |
|
247 | if self.LDAP_BIND_DN and self.LDAP_BIND_PASS: | |
239 | log.debug('Trying simple_bind with password and given DN: %s', |
|
248 | log.debug('Trying simple_bind with password and given login DN: %s', | |
240 | self.LDAP_BIND_DN) |
|
249 | self.LDAP_BIND_DN) | |
241 | server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS) |
|
250 | server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS) | |
242 |
|
251 | |||
243 | return server |
|
252 | return server | |
244 |
|
253 | |||
245 | def get_uid(self, username): |
|
254 | def get_uid(self, username): | |
246 | from rhodecode.lib.helpers import chop_at |
|
|||
247 | uid = username |
|
255 | uid = username | |
248 | for server_addr in self.SERVER_ADDRESSES: |
|
256 | for server_addr in self.SERVER_ADDRESSES: | |
249 | uid = chop_at(username, "@%s" % server_addr) |
|
257 | uid = chop_at(username, "@%s" % server_addr) | |
@@ -292,8 +300,11 b' class AuthLdap(object):' | |||||
292 | self.BASE_DN, self.SEARCH_SCOPE, filter_) |
|
300 | self.BASE_DN, self.SEARCH_SCOPE, filter_) | |
293 |
|
301 | |||
294 | if not lobjects: |
|
302 | if not lobjects: | |
|
303 | log.debug("No matching LDAP objects for authentication " | |||
|
304 | "of UID:'%s' username:(%s)", uid, username) | |||
295 | raise ldap.NO_SUCH_OBJECT() |
|
305 | raise ldap.NO_SUCH_OBJECT() | |
296 |
|
306 | |||
|
307 | log.debug('Found matching ldap object, trying to authenticate') | |||
297 | for (dn, _attrs) in lobjects: |
|
308 | for (dn, _attrs) in lobjects: | |
298 | if dn is None: |
|
309 | if dn is None: | |
299 | continue |
|
310 | continue | |
@@ -304,15 +315,13 b' class AuthLdap(object):' | |||||
304 | break |
|
315 | break | |
305 |
|
316 | |||
306 | else: |
|
317 | else: | |
307 | log.debug("No matching LDAP objects for authentication " |
|
|||
308 | "of '%s' (%s)", uid, username) |
|
|||
309 | raise LdapPasswordError('Failed to authenticate user ' |
|
318 | raise LdapPasswordError('Failed to authenticate user ' | |
310 | 'with given password') |
|
319 | 'with given password') | |
311 |
|
320 | |||
312 | except ldap.NO_SUCH_OBJECT: |
|
321 | except ldap.NO_SUCH_OBJECT: | |
313 | log.debug("LDAP says no such user '%s' (%s), org_exc:", |
|
322 | log.debug("LDAP says no such user '%s' (%s), org_exc:", | |
314 | uid, username, exc_info=True) |
|
323 | uid, username, exc_info=True) | |
315 | raise LdapUsernameError() |
|
324 | raise LdapUsernameError('Unable to find user') | |
316 | except ldap.SERVER_DOWN: |
|
325 | except ldap.SERVER_DOWN: | |
317 | org_exc = traceback.format_exc() |
|
326 | org_exc = traceback.format_exc() | |
318 | raise LdapConnectionError( |
|
327 | raise LdapConnectionError( | |
@@ -447,7 +456,7 b' class RhodeCodeAuthPlugin(RhodeCodeExter' | |||||
447 | 'email': get_ldap_attr('attr_email') or email, |
|
456 | 'email': get_ldap_attr('attr_email') or email, | |
448 | 'admin': admin, |
|
457 | 'admin': admin, | |
449 | 'active': active, |
|
458 | 'active': active, | |
450 |
|
|
459 | 'active_from_extern': None, | |
451 | 'extern_name': user_dn, |
|
460 | 'extern_name': user_dn, | |
452 | 'extern_type': extern_type, |
|
461 | 'extern_type': extern_type, | |
453 | } |
|
462 | } |
@@ -17,6 +17,7 b'' | |||||
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 | RhodeCode authentication library for PAM |
|
22 | RhodeCode authentication library for PAM | |
22 | """ |
|
23 | """ | |
@@ -29,10 +30,9 b' import pwd' | |||||
29 | import re |
|
30 | import re | |
30 | import socket |
|
31 | import socket | |
31 |
|
32 | |||
32 |
from |
|
33 | from rhodecode.translation import _ | |
33 | from sqlalchemy.ext.hybrid import hybrid_property |
|
34 | from rhodecode.authentication.base import ( | |
34 |
|
35 | RhodeCodeExternalAuthPlugin, hybrid_property) | ||
35 | from rhodecode.authentication.base import RhodeCodeExternalAuthPlugin |
|
|||
36 | from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase |
|
36 | from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase | |
37 | from rhodecode.authentication.routes import AuthnPluginResourceBase |
|
37 | from rhodecode.authentication.routes import AuthnPluginResourceBase | |
38 | from rhodecode.lib.colander_utils import strip_whitespace |
|
38 | from rhodecode.lib.colander_utils import strip_whitespace |
@@ -25,9 +25,8 b' RhodeCode authentication plugin for buil' | |||||
25 | import logging |
|
25 | import logging | |
26 |
|
26 | |||
27 | from pylons.i18n.translation import lazy_ugettext as _ |
|
27 | from pylons.i18n.translation import lazy_ugettext as _ | |
28 | from sqlalchemy.ext.hybrid import hybrid_property |
|
|||
29 |
|
28 | |||
30 | from rhodecode.authentication.base import RhodeCodeAuthPluginBase |
|
29 | from rhodecode.authentication.base import RhodeCodeAuthPluginBase, hybrid_property | |
31 | from rhodecode.authentication.routes import AuthnPluginResourceBase |
|
30 | from rhodecode.authentication.routes import AuthnPluginResourceBase | |
32 | from rhodecode.lib.utils2 import safe_str |
|
31 | from rhodecode.lib.utils2 import safe_str | |
33 | from rhodecode.model.db import User |
|
32 | from rhodecode.model.db import User |
@@ -24,10 +24,9 b' RhodeCode authentication token plugin fo' | |||||
24 |
|
24 | |||
25 | import logging |
|
25 | import logging | |
26 |
|
26 | |||
27 | from sqlalchemy.ext.hybrid import hybrid_property |
|
|||
28 |
|
||||
29 | from rhodecode.translation import _ |
|
27 | from rhodecode.translation import _ | |
30 |
from rhodecode.authentication.base import |
|
28 | from rhodecode.authentication.base import ( | |
|
29 | RhodeCodeAuthPluginBase, VCS_TYPE, hybrid_property) | |||
31 | from rhodecode.authentication.routes import AuthnPluginResourceBase |
|
30 | from rhodecode.authentication.routes import AuthnPluginResourceBase | |
32 | from rhodecode.model.db import User, UserApiKeys |
|
31 | from rhodecode.model.db import User, UserApiKeys | |
33 |
|
32 |
@@ -40,10 +40,11 b' class AuthnPluginSettingsSchemaBase(cola' | |||||
40 | cache_ttl = colander.SchemaNode( |
|
40 | cache_ttl = colander.SchemaNode( | |
41 | colander.Int(), |
|
41 | colander.Int(), | |
42 | default=0, |
|
42 | default=0, | |
43 | description=_('Amount of seconds to cache the authentication ' |
|
43 | description=_('Amount of seconds to cache the authentication response' | |
44 |
'call for this plugin. |
|
44 | 'call for this plugin. \n' | |
45 |
'LDAP to improve the |
|
45 | 'Useful for long calls like LDAP to improve the ' | |
46 |
'authentication system |
|
46 | 'performance of the authentication system ' | |
|
47 | '(0 means disabled).'), | |||
47 | missing=0, |
|
48 | missing=0, | |
48 | title=_('Auth Cache TTL'), |
|
49 | title=_('Auth Cache TTL'), | |
49 | validator=colander.Range(min=0, max=None), |
|
50 | validator=colander.Range(min=0, max=None), |
@@ -57,7 +57,12 b' class RhodecodeEvent(object):' | |||||
57 | def actor(self): |
|
57 | def actor(self): | |
58 | auth_user = self.auth_user |
|
58 | auth_user = self.auth_user | |
59 | if auth_user: |
|
59 | if auth_user: | |
60 |
|
|
60 | instance = auth_user.get_instance() | |
|
61 | if not instance: | |||
|
62 | return AttributeDict(dict( | |||
|
63 | username=auth_user.username | |||
|
64 | )) | |||
|
65 | ||||
61 | return SYSTEM_USER |
|
66 | return SYSTEM_USER | |
62 |
|
67 | |||
63 | @property |
|
68 | @property |
@@ -61,6 +61,7 b'' | |||||
61 | .help-block { |
|
61 | .help-block { | |
62 | display: block; |
|
62 | display: block; | |
63 | margin-left: @label-width; |
|
63 | margin-left: @label-width; | |
|
64 | white-space: pre; | |||
64 | } |
|
65 | } | |
65 |
|
66 | |||
66 | .action_button { |
|
67 | .action_button { |
@@ -111,6 +111,7 b'' | |||||
111 | $("#tls_kind").select2(select2Options); |
|
111 | $("#tls_kind").select2(select2Options); | |
112 | $("#tls_reqcert").select2(select2Options); |
|
112 | $("#tls_reqcert").select2(select2Options); | |
113 | $("#search_scope").select2(select2Options); |
|
113 | $("#search_scope").select2(select2Options); | |
|
114 | $("#group_extraction_type").select2(select2Options); | |||
114 | }); |
|
115 | }); | |
115 | </script> |
|
116 | </script> | |
116 | </%def> |
|
117 | </%def> |
General Comments 0
You need to be logged in to leave comments.
Login now