##// END OF EJS Templates
tests: password reset hidden/disabled options tests fix #3944
lisaq -
r1036:54572a14 default
parent child Browse files
Show More
@@ -1,653 +1,653 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="root.html"/>
3 3
4 4 <div class="outerwrapper">
5 5 <!-- HEADER -->
6 6 <div class="header">
7 7 <div id="header-inner" class="wrapper">
8 8 <div id="logo">
9 9 <div class="logo-wrapper">
10 10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
11 11 </div>
12 12 %if c.rhodecode_name:
13 13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
14 14 %endif
15 15 </div>
16 16 <!-- MENU BAR NAV -->
17 17 ${self.menu_bar_nav()}
18 18 <!-- END MENU BAR NAV -->
19 19 </div>
20 20 </div>
21 21 ${self.menu_bar_subnav()}
22 22 <!-- END HEADER -->
23 23
24 24 <!-- CONTENT -->
25 25 <div id="content" class="wrapper">
26 26 <div class="main">
27 27 ${next.main()}
28 28 </div>
29 29 </div>
30 30 <!-- END CONTENT -->
31 31
32 32 </div>
33 33 <!-- FOOTER -->
34 34 <div id="footer">
35 35 <div id="footer-inner" class="title wrapper">
36 36 <div>
37 37 <p class="footer-link-right">
38 38 % if c.visual.show_version:
39 39 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
40 40 % endif
41 41 &copy; 2010-${h.datetime.today().year}, <a href="${h.url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
42 42 % if c.visual.rhodecode_support_url:
43 43 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
44 44 % endif
45 45 </p>
46 46 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
47 47 <p class="server-instance" style="display:${sid}">
48 48 ## display hidden instance ID if specially defined
49 49 % if c.rhodecode_instanceid:
50 50 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
51 51 % endif
52 52 </p>
53 53 </div>
54 54 </div>
55 55 </div>
56 56
57 57 <!-- END FOOTER -->
58 58
59 59 ### MAKO DEFS ###
60 60
61 61 <%def name="menu_bar_subnav()">
62 62 </%def>
63 63
64 64 <%def name="breadcrumbs(class_='breadcrumbs')">
65 65 <div class="${class_}">
66 66 ${self.breadcrumbs_links()}
67 67 </div>
68 68 </%def>
69 69
70 70 <%def name="admin_menu()">
71 71 <ul class="admin_menu submenu">
72 72 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
73 73 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
74 74 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
75 75 <li><a href="${h.url('users')}">${_('Users')}</a></li>
76 76 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
77 77 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
78 78 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
79 79 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
80 80 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
81 81 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
82 82 </ul>
83 83 </%def>
84 84
85 85
86 86 <%def name="dt_info_panel(elements)">
87 87 <dl class="dl-horizontal">
88 88 %for dt, dd, title, show_items in elements:
89 89 <dt>${dt}:</dt>
90 90 <dd title="${title}">
91 91 %if callable(dd):
92 92 ## allow lazy evaluation of elements
93 93 ${dd()}
94 94 %else:
95 95 ${dd}
96 96 %endif
97 97 %if show_items:
98 98 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
99 99 %endif
100 100 </dd>
101 101
102 102 %if show_items:
103 103 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
104 104 %for item in show_items:
105 105 <dt></dt>
106 106 <dd>${item}</dd>
107 107 %endfor
108 108 </div>
109 109 %endif
110 110
111 111 %endfor
112 112 </dl>
113 113 </%def>
114 114
115 115
116 116 <%def name="gravatar(email, size=16)">
117 117 <%
118 118 if (size > 16):
119 119 gravatar_class = 'gravatar gravatar-large'
120 120 else:
121 121 gravatar_class = 'gravatar'
122 122 %>
123 123 <%doc>
124 124 TODO: johbo: For now we serve double size images to make it smooth
125 125 for retina. This is how it worked until now. Should be replaced
126 126 with a better solution at some point.
127 127 </%doc>
128 128 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
129 129 </%def>
130 130
131 131
132 132 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
133 133 <% email = h.email_or_none(contact) %>
134 134 <div class="rc-user tooltip" title="${h.author_string(email)}">
135 135 ${self.gravatar(email, size)}
136 136 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
137 137 </div>
138 138 </%def>
139 139
140 140
141 141 ## admin menu used for people that have some admin resources
142 142 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
143 143 <ul class="submenu">
144 144 %if repositories:
145 145 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
146 146 %endif
147 147 %if repository_groups:
148 148 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
149 149 %endif
150 150 %if user_groups:
151 151 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
152 152 %endif
153 153 </ul>
154 154 </%def>
155 155
156 156 <%def name="repo_page_title(repo_instance)">
157 157 <div class="title-content">
158 158 <div class="title-main">
159 159 ## SVN/HG/GIT icons
160 160 %if h.is_hg(repo_instance):
161 161 <i class="icon-hg"></i>
162 162 %endif
163 163 %if h.is_git(repo_instance):
164 164 <i class="icon-git"></i>
165 165 %endif
166 166 %if h.is_svn(repo_instance):
167 167 <i class="icon-svn"></i>
168 168 %endif
169 169
170 170 ## public/private
171 171 %if repo_instance.private:
172 172 <i class="icon-repo-private"></i>
173 173 %else:
174 174 <i class="icon-repo-public"></i>
175 175 %endif
176 176
177 177 ## repo name with group name
178 178 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
179 179
180 180 </div>
181 181
182 182 ## FORKED
183 183 %if repo_instance.fork:
184 184 <p>
185 185 <i class="icon-code-fork"></i> ${_('Fork of')}
186 186 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
187 187 </p>
188 188 %endif
189 189
190 190 ## IMPORTED FROM REMOTE
191 191 %if repo_instance.clone_uri:
192 192 <p>
193 193 <i class="icon-code-fork"></i> ${_('Clone from')}
194 194 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
195 195 </p>
196 196 %endif
197 197
198 198 ## LOCKING STATUS
199 199 %if repo_instance.locked[0]:
200 200 <p class="locking_locked">
201 201 <i class="icon-repo-lock"></i>
202 202 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
203 203 </p>
204 204 %elif repo_instance.enable_locking:
205 205 <p class="locking_unlocked">
206 206 <i class="icon-repo-unlock"></i>
207 207 ${_('Repository not locked. Pull repository to lock it.')}
208 208 </p>
209 209 %endif
210 210
211 211 </div>
212 212 </%def>
213 213
214 214 <%def name="repo_menu(active=None)">
215 215 <%
216 216 def is_active(selected):
217 217 if selected == active:
218 218 return "active"
219 219 %>
220 220
221 221 <!--- CONTEXT BAR -->
222 222 <div id="context-bar">
223 223 <div class="wrapper">
224 224 <ul id="context-pages" class="horizontal-list navigation">
225 225 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
226 226 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
227 227 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
228 228 <li class="${is_active('compare')}">
229 229 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
230 230 </li>
231 231 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
232 232 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
233 233 <li class="${is_active('showpullrequest')}">
234 234 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
235 235 %if c.repository_pull_requests:
236 236 <span class="pr_notifications">${c.repository_pull_requests}</span>
237 237 %endif
238 238 <div class="menulabel">${_('Pull Requests')}</div>
239 239 </a>
240 240 </li>
241 241 %endif
242 242 <li class="${is_active('options')}">
243 243 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
244 244 <ul class="submenu">
245 245 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
246 246 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
247 247 %endif
248 248 %if c.rhodecode_db_repo.fork:
249 249 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
250 250 ${_('Compare fork')}</a></li>
251 251 %endif
252 252
253 253 <li><a href="${h.url('search_repo_home',repo_name=c.repo_name)}">${_('Search')}</a></li>
254 254
255 255 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
256 256 %if c.rhodecode_db_repo.locked[0]:
257 257 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
258 258 %else:
259 259 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
260 260 %endif
261 261 %endif
262 262 %if c.rhodecode_user.username != h.DEFAULT_USER:
263 263 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
264 264 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
265 265 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
266 266 %endif
267 267 %endif
268 268 </ul>
269 269 </li>
270 270 </ul>
271 271 </div>
272 272 <div class="clear"></div>
273 273 </div>
274 274 <!--- END CONTEXT BAR -->
275 275
276 276 </%def>
277 277
278 278 <%def name="usermenu()">
279 279 ## USER MENU
280 280 <li id="quick_login_li">
281 281 <a id="quick_login_link" class="menulink childs">
282 282 ${gravatar(c.rhodecode_user.email, 20)}
283 283 <span class="user">
284 284 %if c.rhodecode_user.username != h.DEFAULT_USER:
285 285 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
286 286 %else:
287 287 <span>${_('Sign in')}</span>
288 288 %endif
289 289 </span>
290 290 </a>
291 291
292 292 <div class="user-menu submenu">
293 293 <div id="quick_login">
294 294 %if c.rhodecode_user.username == h.DEFAULT_USER:
295 295 <h4>${_('Sign in to your account')}</h4>
296 296 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
297 297 <div class="form form-vertical">
298 298 <div class="fields">
299 299 <div class="field">
300 300 <div class="label">
301 301 <label for="username">${_('Username')}:</label>
302 302 </div>
303 303 <div class="input">
304 304 ${h.text('username',class_='focus',tabindex=1)}
305 305 </div>
306 306
307 307 </div>
308 308 <div class="field">
309 309 <div class="label">
310 310 <label for="password">${_('Password')}:</label>
311 311 %if h.HasPermissionAny('hg.password_reset.enabled')():
312 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'))}</span>
312 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
313 313 %endif
314 314 </div>
315 315 <div class="input">
316 316 ${h.password('password',class_='focus',tabindex=2)}
317 317 </div>
318 318 </div>
319 319 <div class="buttons">
320 320 <div class="register">
321 321 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
322 322 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
323 323 %endif
324 324 </div>
325 325 <div class="submit">
326 326 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
327 327 </div>
328 328 </div>
329 329 </div>
330 330 </div>
331 331 ${h.end_form()}
332 332 %else:
333 333 <div class="">
334 334 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
335 335 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
336 336 <div class="email">${c.rhodecode_user.email}</div>
337 337 </div>
338 338 <div class="">
339 339 <ol class="links">
340 340 <li>${h.link_to(_(u'My account'),h.url('my_account'))}</li>
341 341 <li class="logout">
342 342 ${h.secure_form(h.route_path('logout'))}
343 343 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
344 344 ${h.end_form()}
345 345 </li>
346 346 </ol>
347 347 </div>
348 348 %endif
349 349 </div>
350 350 </div>
351 351 %if c.rhodecode_user.username != h.DEFAULT_USER:
352 352 <div class="pill_container">
353 353 % if c.unread_notifications == 0:
354 354 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
355 355 % else:
356 356 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
357 357 % endif
358 358 </div>
359 359 % endif
360 360 </li>
361 361 </%def>
362 362
363 363 <%def name="menu_items(active=None)">
364 364 <%
365 365 def is_active(selected):
366 366 if selected == active:
367 367 return "active"
368 368 return ""
369 369 %>
370 370 <ul id="quick" class="main_nav navigation horizontal-list">
371 371 <!-- repo switcher -->
372 372 <li class="${is_active('repositories')} repo_switcher_li has_select2">
373 373 <input id="repo_switcher" name="repo_switcher" type="hidden">
374 374 </li>
375 375
376 376 ## ROOT MENU
377 377 %if c.rhodecode_user.username != h.DEFAULT_USER:
378 378 <li class="${is_active('journal')}">
379 379 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
380 380 <div class="menulabel">${_('Journal')}</div>
381 381 </a>
382 382 </li>
383 383 %else:
384 384 <li class="${is_active('journal')}">
385 385 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
386 386 <div class="menulabel">${_('Public journal')}</div>
387 387 </a>
388 388 </li>
389 389 %endif
390 390 <li class="${is_active('gists')}">
391 391 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
392 392 <div class="menulabel">${_('Gists')}</div>
393 393 </a>
394 394 </li>
395 395 <li class="${is_active('search')}">
396 396 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.url('search')}">
397 397 <div class="menulabel">${_('Search')}</div>
398 398 </a>
399 399 </li>
400 400 % if h.HasPermissionAll('hg.admin')('access admin main page'):
401 401 <li class="${is_active('admin')}">
402 402 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
403 403 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
404 404 </a>
405 405 ${admin_menu()}
406 406 </li>
407 407 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
408 408 <li class="${is_active('admin')}">
409 409 <a class="menulink childs" title="${_('Delegated Admin settings')}">
410 410 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
411 411 </a>
412 412 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
413 413 c.rhodecode_user.repository_groups_admin,
414 414 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
415 415 </li>
416 416 % endif
417 417 % if c.debug_style:
418 418 <li class="${is_active('debug_style')}">
419 419 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
420 420 <div class="menulabel">${_('Style')}</div>
421 421 </a>
422 422 </li>
423 423 % endif
424 424 ## render extra user menu
425 425 ${usermenu()}
426 426 </ul>
427 427
428 428 <script type="text/javascript">
429 429 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
430 430
431 431 /*format the look of items in the list*/
432 432 var format = function(state, escapeMarkup){
433 433 if (!state.id){
434 434 return state.text; // optgroup
435 435 }
436 436 var obj_dict = state.obj;
437 437 var tmpl = '';
438 438
439 439 if(obj_dict && state.type == 'repo'){
440 440 if(obj_dict['repo_type'] === 'hg'){
441 441 tmpl += '<i class="icon-hg"></i> ';
442 442 }
443 443 else if(obj_dict['repo_type'] === 'git'){
444 444 tmpl += '<i class="icon-git"></i> ';
445 445 }
446 446 else if(obj_dict['repo_type'] === 'svn'){
447 447 tmpl += '<i class="icon-svn"></i> ';
448 448 }
449 449 if(obj_dict['private']){
450 450 tmpl += '<i class="icon-lock" ></i> ';
451 451 }
452 452 else if(visual_show_public_icon){
453 453 tmpl += '<i class="icon-unlock-alt"></i> ';
454 454 }
455 455 }
456 456 if(obj_dict && state.type == 'commit') {
457 457 tmpl += '<i class="icon-tag"></i>';
458 458 }
459 459 if(obj_dict && state.type == 'group'){
460 460 tmpl += '<i class="icon-folder-close"></i> ';
461 461 }
462 462 tmpl += escapeMarkup(state.text);
463 463 return tmpl;
464 464 };
465 465
466 466 var formatResult = function(result, container, query, escapeMarkup) {
467 467 return format(result, escapeMarkup);
468 468 };
469 469
470 470 var formatSelection = function(data, container, escapeMarkup) {
471 471 return format(data, escapeMarkup);
472 472 };
473 473
474 474 $("#repo_switcher").select2({
475 475 cachedDataSource: {},
476 476 minimumInputLength: 2,
477 477 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
478 478 dropdownAutoWidth: true,
479 479 formatResult: formatResult,
480 480 formatSelection: formatSelection,
481 481 containerCssClass: "repo-switcher",
482 482 dropdownCssClass: "repo-switcher-dropdown",
483 483 escapeMarkup: function(m){
484 484 // don't escape our custom placeholder
485 485 if(m.substr(0,23) == '<div class="menulabel">'){
486 486 return m;
487 487 }
488 488
489 489 return Select2.util.escapeMarkup(m);
490 490 },
491 491 query: $.debounce(250, function(query){
492 492 self = this;
493 493 var cacheKey = query.term;
494 494 var cachedData = self.cachedDataSource[cacheKey];
495 495
496 496 if (cachedData) {
497 497 query.callback({results: cachedData.results});
498 498 } else {
499 499 $.ajax({
500 500 url: "${h.url('goto_switcher_data')}",
501 501 data: {'query': query.term},
502 502 dataType: 'json',
503 503 type: 'GET',
504 504 success: function(data) {
505 505 self.cachedDataSource[cacheKey] = data;
506 506 query.callback({results: data.results});
507 507 },
508 508 error: function(data, textStatus, errorThrown) {
509 509 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
510 510 }
511 511 })
512 512 }
513 513 })
514 514 });
515 515
516 516 $("#repo_switcher").on('select2-selecting', function(e){
517 517 e.preventDefault();
518 518 window.location = e.choice.url;
519 519 });
520 520
521 521 ## Global mouse bindings ##
522 522
523 523 // general help "?"
524 524 Mousetrap.bind(['?'], function(e) {
525 525 $('#help_kb').modal({})
526 526 });
527 527
528 528 // / open the quick filter
529 529 Mousetrap.bind(['/'], function(e) {
530 530 $("#repo_switcher").select2("open");
531 531
532 532 // return false to prevent default browser behavior
533 533 // and stop event from bubbling
534 534 return false;
535 535 });
536 536
537 537 // general nav g + action
538 538 Mousetrap.bind(['g h'], function(e) {
539 539 window.location = pyroutes.url('home');
540 540 });
541 541 Mousetrap.bind(['g g'], function(e) {
542 542 window.location = pyroutes.url('gists', {'private':1});
543 543 });
544 544 Mousetrap.bind(['g G'], function(e) {
545 545 window.location = pyroutes.url('gists', {'public':1});
546 546 });
547 547 Mousetrap.bind(['n g'], function(e) {
548 548 window.location = pyroutes.url('new_gist');
549 549 });
550 550 Mousetrap.bind(['n r'], function(e) {
551 551 window.location = pyroutes.url('new_repo');
552 552 });
553 553
554 554 % if hasattr(c, 'repo_name') and hasattr(c, 'rhodecode_db_repo'):
555 555 // nav in repo context
556 556 Mousetrap.bind(['g s'], function(e) {
557 557 window.location = pyroutes.url('summary_home', {'repo_name': REPO_NAME});
558 558 });
559 559 Mousetrap.bind(['g c'], function(e) {
560 560 window.location = pyroutes.url('changelog_home', {'repo_name': REPO_NAME});
561 561 });
562 562 Mousetrap.bind(['g F'], function(e) {
563 563 window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.rhodecode_db_repo.landing_rev[1]}', 'f_path': '', 'search': '1'});
564 564 });
565 565 Mousetrap.bind(['g f'], function(e) {
566 566 window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.rhodecode_db_repo.landing_rev[1]}', 'f_path': ''});
567 567 });
568 568 Mousetrap.bind(['g p'], function(e) {
569 569 window.location = pyroutes.url('pullrequest_show_all', {'repo_name': REPO_NAME});
570 570 });
571 571 Mousetrap.bind(['g o'], function(e) {
572 572 window.location = pyroutes.url('edit_repo', {'repo_name': REPO_NAME});
573 573 });
574 574 Mousetrap.bind(['g O'], function(e) {
575 575 window.location = pyroutes.url('edit_repo_perms', {'repo_name': REPO_NAME});
576 576 });
577 577 % endif
578 578
579 579 </script>
580 580 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
581 581 </%def>
582 582
583 583 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
584 584 <div class="modal-dialog">
585 585 <div class="modal-content">
586 586 <div class="modal-header">
587 587 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
588 588 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
589 589 </div>
590 590 <div class="modal-body">
591 591 <div class="block-left">
592 592 <table class="keyboard-mappings">
593 593 <tbody>
594 594 <tr>
595 595 <th></th>
596 596 <th>${_('Site-wide shortcuts')}</th>
597 597 </tr>
598 598 <%
599 599 elems = [
600 600 ('/', 'Open quick search box'),
601 601 ('g h', 'Goto home page'),
602 602 ('g g', 'Goto my private gists page'),
603 603 ('g G', 'Goto my public gists page'),
604 604 ('n r', 'New repository page'),
605 605 ('n g', 'New gist page'),
606 606 ]
607 607 %>
608 608 %for key, desc in elems:
609 609 <tr>
610 610 <td class="keys">
611 611 <span class="key tag">${key}</span>
612 612 </td>
613 613 <td>${desc}</td>
614 614 </tr>
615 615 %endfor
616 616 </tbody>
617 617 </table>
618 618 </div>
619 619 <div class="block-left">
620 620 <table class="keyboard-mappings">
621 621 <tbody>
622 622 <tr>
623 623 <th></th>
624 624 <th>${_('Repositories')}</th>
625 625 </tr>
626 626 <%
627 627 elems = [
628 628 ('g s', 'Goto summary page'),
629 629 ('g c', 'Goto changelog page'),
630 630 ('g f', 'Goto files page'),
631 631 ('g F', 'Goto files page with file search activated'),
632 632 ('g p', 'Goto pull requests page'),
633 633 ('g o', 'Goto repository settings'),
634 634 ('g O', 'Goto repository permissions settings'),
635 635 ]
636 636 %>
637 637 %for key, desc in elems:
638 638 <tr>
639 639 <td class="keys">
640 640 <span class="key tag">${key}</span>
641 641 </td>
642 642 <td>${desc}</td>
643 643 </tr>
644 644 %endfor
645 645 </tbody>
646 646 </table>
647 647 </div>
648 648 </div>
649 649 <div class="modal-footer">
650 650 </div>
651 651 </div><!-- /.modal-content -->
652 652 </div><!-- /.modal-dialog -->
653 653 </div><!-- /.modal -->
@@ -1,519 +1,588 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import urlparse
22 22
23 23 import mock
24 24 import pytest
25 25
26 26 from rhodecode.config.routing import ADMIN_PREFIX
27 27 from rhodecode.tests import (
28 assert_session_flash, url, HG_REPO, TEST_USER_ADMIN_LOGIN)
28 TestController, assert_session_flash, clear_all_caches, url,
29 HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
29 30 from rhodecode.tests.fixture import Fixture
30 31 from rhodecode.tests.utils import AssertResponse, get_session_from_response
31 32 from rhodecode.lib.auth import check_password, generate_auth_token
32 33 from rhodecode.lib import helpers as h
33 34 from rhodecode.model.auth_token import AuthTokenModel
34 35 from rhodecode.model import validators
35 36 from rhodecode.model.db import User, Notification
36 37 from rhodecode.model.meta import Session
37 38
38 39 fixture = Fixture()
39 40
40 41 # Hardcode URLs because we don't have a request object to use
41 42 # pyramids URL generation methods.
43 index_url = '/'
42 44 login_url = ADMIN_PREFIX + '/login'
43 45 logut_url = ADMIN_PREFIX + '/logout'
44 46 register_url = ADMIN_PREFIX + '/register'
45 47 pwd_reset_url = ADMIN_PREFIX + '/password_reset'
46 48 pwd_reset_confirm_url = ADMIN_PREFIX + '/password_reset_confirmation'
47 49
48 50
49 51 @pytest.mark.usefixtures('app')
50 52 class TestLoginController:
51 53 destroy_users = set()
52 54
53 55 @classmethod
54 56 def teardown_class(cls):
55 57 fixture.destroy_users(cls.destroy_users)
56 58
57 59 def teardown_method(self, method):
58 60 for n in Notification.query().all():
59 61 Session().delete(n)
60 62
61 63 Session().commit()
62 64 assert Notification.query().all() == []
63 65
64 66 def test_index(self):
65 67 response = self.app.get(login_url)
66 68 assert response.status == '200 OK'
67 69 # Test response...
68 70
69 71 def test_login_admin_ok(self):
70 72 response = self.app.post(login_url,
71 73 {'username': 'test_admin',
72 74 'password': 'test12'})
73 75 assert response.status == '302 Found'
74 76 session = get_session_from_response(response)
75 77 username = session['rhodecode_user'].get('username')
76 78 assert username == 'test_admin'
77 79 response = response.follow()
78 80 response.mustcontain('/%s' % HG_REPO)
79 81
80 82 def test_login_regular_ok(self):
81 83 response = self.app.post(login_url,
82 84 {'username': 'test_regular',
83 85 'password': 'test12'})
84 86
85 87 assert response.status == '302 Found'
86 88 session = get_session_from_response(response)
87 89 username = session['rhodecode_user'].get('username')
88 90 assert username == 'test_regular'
89 91 response = response.follow()
90 92 response.mustcontain('/%s' % HG_REPO)
91 93
92 94 def test_login_ok_came_from(self):
93 95 test_came_from = '/_admin/users?branch=stable'
94 96 _url = '{}?came_from={}'.format(login_url, test_came_from)
95 97 response = self.app.post(
96 98 _url, {'username': 'test_admin', 'password': 'test12'})
97 99 assert response.status == '302 Found'
98 100 assert 'branch=stable' in response.location
99 101 response = response.follow()
100 102
101 103 assert response.status == '200 OK'
102 104 response.mustcontain('Users administration')
103 105
104 106 def test_redirect_to_login_with_get_args(self):
105 107 with fixture.anon_access(False):
106 108 kwargs = {'branch': 'stable'}
107 109 response = self.app.get(
108 110 url('summary_home', repo_name=HG_REPO, **kwargs))
109 111 assert response.status == '302 Found'
110 112 response_query = urlparse.parse_qsl(response.location)
111 113 assert 'branch=stable' in response_query[0][1]
112 114
113 115 def test_login_form_with_get_args(self):
114 116 _url = '{}?came_from=/_admin/users,branch=stable'.format(login_url)
115 117 response = self.app.get(_url)
116 118 assert 'branch%3Dstable' in response.form.action
117 119
118 120 @pytest.mark.parametrize("url_came_from", [
119 121 'data:text/html,<script>window.alert("xss")</script>',
120 122 'mailto:test@rhodecode.org',
121 123 'file:///etc/passwd',
122 124 'ftp://some.ftp.server',
123 125 'http://other.domain',
124 126 '/\r\nX-Forwarded-Host: http://example.org',
125 127 ])
126 128 def test_login_bad_came_froms(self, url_came_from):
127 129 _url = '{}?came_from={}'.format(login_url, url_came_from)
128 130 response = self.app.post(
129 131 _url,
130 132 {'username': 'test_admin', 'password': 'test12'})
131 133 assert response.status == '302 Found'
132 134 response = response.follow()
133 135 assert response.status == '200 OK'
134 136 assert response.request.path == '/'
135 137
136 138 def test_login_short_password(self):
137 139 response = self.app.post(login_url,
138 140 {'username': 'test_admin',
139 141 'password': 'as'})
140 142 assert response.status == '200 OK'
141 143
142 144 response.mustcontain('Enter 3 characters or more')
143 145
144 146 def test_login_wrong_non_ascii_password(self, user_regular):
145 147 response = self.app.post(
146 148 login_url,
147 149 {'username': user_regular.username,
148 150 'password': u'invalid-non-asci\xe4'.encode('utf8')})
149 151
150 152 response.mustcontain('invalid user name')
151 153 response.mustcontain('invalid password')
152 154
153 155 def test_login_with_non_ascii_password(self, user_util):
154 156 password = u'valid-non-ascii\xe4'
155 157 user = user_util.create_user(password=password)
156 158 response = self.app.post(
157 159 login_url,
158 160 {'username': user.username,
159 161 'password': password.encode('utf-8')})
160 162 assert response.status_code == 302
161 163
162 164 def test_login_wrong_username_password(self):
163 165 response = self.app.post(login_url,
164 166 {'username': 'error',
165 167 'password': 'test12'})
166 168
167 169 response.mustcontain('invalid user name')
168 170 response.mustcontain('invalid password')
169 171
170 172 def test_login_admin_ok_password_migration(self, real_crypto_backend):
171 173 from rhodecode.lib import auth
172 174
173 175 # create new user, with sha256 password
174 176 temp_user = 'test_admin_sha256'
175 177 user = fixture.create_user(temp_user)
176 178 user.password = auth._RhodeCodeCryptoSha256().hash_create(
177 179 b'test123')
178 180 Session().add(user)
179 181 Session().commit()
180 182 self.destroy_users.add(temp_user)
181 183 response = self.app.post(login_url,
182 184 {'username': temp_user,
183 185 'password': 'test123'})
184 186
185 187 assert response.status == '302 Found'
186 188 session = get_session_from_response(response)
187 189 username = session['rhodecode_user'].get('username')
188 190 assert username == temp_user
189 191 response = response.follow()
190 192 response.mustcontain('/%s' % HG_REPO)
191 193
192 194 # new password should be bcrypted, after log-in and transfer
193 195 user = User.get_by_username(temp_user)
194 196 assert user.password.startswith('$')
195 197
196 198 # REGISTRATIONS
197 199 def test_register(self):
198 200 response = self.app.get(register_url)
199 201 response.mustcontain('Create an Account')
200 202
201 203 def test_register_err_same_username(self):
202 204 uname = 'test_admin'
203 205 response = self.app.post(
204 206 register_url,
205 207 {
206 208 'username': uname,
207 209 'password': 'test12',
208 210 'password_confirmation': 'test12',
209 211 'email': 'goodmail@domain.com',
210 212 'firstname': 'test',
211 213 'lastname': 'test'
212 214 }
213 215 )
214 216
215 217 assertr = AssertResponse(response)
216 218 msg = validators.ValidUsername()._messages['username_exists']
217 219 msg = msg % {'username': uname}
218 220 assertr.element_contains('#username+.error-message', msg)
219 221
220 222 def test_register_err_same_email(self):
221 223 response = self.app.post(
222 224 register_url,
223 225 {
224 226 'username': 'test_admin_0',
225 227 'password': 'test12',
226 228 'password_confirmation': 'test12',
227 229 'email': 'test_admin@mail.com',
228 230 'firstname': 'test',
229 231 'lastname': 'test'
230 232 }
231 233 )
232 234
233 235 assertr = AssertResponse(response)
234 236 msg = validators.UniqSystemEmail()()._messages['email_taken']
235 237 assertr.element_contains('#email+.error-message', msg)
236 238
237 239 def test_register_err_same_email_case_sensitive(self):
238 240 response = self.app.post(
239 241 register_url,
240 242 {
241 243 'username': 'test_admin_1',
242 244 'password': 'test12',
243 245 'password_confirmation': 'test12',
244 246 'email': 'TesT_Admin@mail.COM',
245 247 'firstname': 'test',
246 248 'lastname': 'test'
247 249 }
248 250 )
249 251 assertr = AssertResponse(response)
250 252 msg = validators.UniqSystemEmail()()._messages['email_taken']
251 253 assertr.element_contains('#email+.error-message', msg)
252 254
253 255 def test_register_err_wrong_data(self):
254 256 response = self.app.post(
255 257 register_url,
256 258 {
257 259 'username': 'xs',
258 260 'password': 'test',
259 261 'password_confirmation': 'test',
260 262 'email': 'goodmailm',
261 263 'firstname': 'test',
262 264 'lastname': 'test'
263 265 }
264 266 )
265 267 assert response.status == '200 OK'
266 268 response.mustcontain('An email address must contain a single @')
267 269 response.mustcontain('Enter a value 6 characters long or more')
268 270
269 271 def test_register_err_username(self):
270 272 response = self.app.post(
271 273 register_url,
272 274 {
273 275 'username': 'error user',
274 276 'password': 'test12',
275 277 'password_confirmation': 'test12',
276 278 'email': 'goodmailm',
277 279 'firstname': 'test',
278 280 'lastname': 'test'
279 281 }
280 282 )
281 283
282 284 response.mustcontain('An email address must contain a single @')
283 285 response.mustcontain(
284 286 'Username may only contain '
285 287 'alphanumeric characters underscores, '
286 288 'periods or dashes and must begin with '
287 289 'alphanumeric character')
288 290
289 291 def test_register_err_case_sensitive(self):
290 292 usr = 'Test_Admin'
291 293 response = self.app.post(
292 294 register_url,
293 295 {
294 296 'username': usr,
295 297 'password': 'test12',
296 298 'password_confirmation': 'test12',
297 299 'email': 'goodmailm',
298 300 'firstname': 'test',
299 301 'lastname': 'test'
300 302 }
301 303 )
302 304
303 305 assertr = AssertResponse(response)
304 306 msg = validators.ValidUsername()._messages['username_exists']
305 307 msg = msg % {'username': usr}
306 308 assertr.element_contains('#username+.error-message', msg)
307 309
308 310 def test_register_special_chars(self):
309 311 response = self.app.post(
310 312 register_url,
311 313 {
312 314 'username': 'xxxaxn',
313 315 'password': 'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
314 316 'password_confirmation': 'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
315 317 'email': 'goodmailm@test.plx',
316 318 'firstname': 'test',
317 319 'lastname': 'test'
318 320 }
319 321 )
320 322
321 323 msg = validators.ValidPassword()._messages['invalid_password']
322 324 response.mustcontain(msg)
323 325
324 326 def test_register_password_mismatch(self):
325 327 response = self.app.post(
326 328 register_url,
327 329 {
328 330 'username': 'xs',
329 331 'password': '123qwe',
330 332 'password_confirmation': 'qwe123',
331 333 'email': 'goodmailm@test.plxa',
332 334 'firstname': 'test',
333 335 'lastname': 'test'
334 336 }
335 337 )
336 338 msg = validators.ValidPasswordsMatch()._messages['password_mismatch']
337 339 response.mustcontain(msg)
338 340
339 341 def test_register_ok(self):
340 342 username = 'test_regular4'
341 343 password = 'qweqwe'
342 344 email = 'marcin@test.com'
343 345 name = 'testname'
344 346 lastname = 'testlastname'
345 347
346 348 response = self.app.post(
347 349 register_url,
348 350 {
349 351 'username': username,
350 352 'password': password,
351 353 'password_confirmation': password,
352 354 'email': email,
353 355 'firstname': name,
354 356 'lastname': lastname,
355 357 'admin': True
356 358 }
357 359 ) # This should be overriden
358 360 assert response.status == '302 Found'
359 361 assert_session_flash(
360 362 response, 'You have successfully registered with RhodeCode')
361 363
362 364 ret = Session().query(User).filter(
363 365 User.username == 'test_regular4').one()
364 366 assert ret.username == username
365 367 assert check_password(password, ret.password)
366 368 assert ret.email == email
367 369 assert ret.name == name
368 370 assert ret.lastname == lastname
369 371 assert ret.api_key is not None
370 372 assert not ret.admin
371 373
372 374 def test_forgot_password_wrong_mail(self):
373 375 bad_email = 'marcin@wrongmail.org'
374 376 response = self.app.post(
375 377 pwd_reset_url,
376 378 {'email': bad_email, }
377 379 )
378 380
379 381 msg = validators.ValidSystemEmail()._messages['non_existing_email']
380 382 msg = h.html_escape(msg % {'email': bad_email})
381 383 response.mustcontain()
382 384
383 385 def test_forgot_password(self):
384 386 response = self.app.get(pwd_reset_url)
385 387 assert response.status == '200 OK'
386 388
387 389 username = 'test_password_reset_1'
388 390 password = 'qweqwe'
389 391 email = 'marcin@python-works.com'
390 392 name = 'passwd'
391 393 lastname = 'reset'
392 394
393 395 new = User()
394 396 new.username = username
395 397 new.password = password
396 398 new.email = email
397 399 new.name = name
398 400 new.lastname = lastname
399 401 new.api_key = generate_auth_token(username)
400 402 Session().add(new)
401 403 Session().commit()
402 404
403 405 response = self.app.post(pwd_reset_url,
404 406 {'email': email, })
405 407
406 408 assert_session_flash(
407 409 response, 'Your password reset link was sent')
408 410
409 411 response = response.follow()
410 412
411 413 # BAD KEY
412 414
413 415 key = "bad"
414 416 confirm_url = '{}?key={}'.format(pwd_reset_confirm_url, key)
415 417 response = self.app.get(confirm_url)
416 418 assert response.status == '302 Found'
417 419 assert response.location.endswith(pwd_reset_url)
418 420
419 421 # GOOD KEY
420 422
421 423 key = User.get_by_username(username).api_key
422 424 confirm_url = '{}?key={}'.format(pwd_reset_confirm_url, key)
423 425 response = self.app.get(confirm_url)
424 426 assert response.status == '302 Found'
425 427 assert response.location.endswith(login_url)
426 428
427 429 assert_session_flash(
428 430 response,
429 431 'Your password reset was successful, '
430 432 'a new password has been sent to your email')
431 433
432 434 response = response.follow()
433 435
434 436 def _get_api_whitelist(self, values=None):
435 437 config = {'api_access_controllers_whitelist': values or []}
436 438 return config
437 439
438 440 @pytest.mark.parametrize("test_name, auth_token", [
439 441 ('none', None),
440 442 ('empty_string', ''),
441 443 ('fake_number', '123456'),
442 444 ('proper_auth_token', None)
443 445 ])
444 446 def test_access_not_whitelisted_page_via_auth_token(self, test_name,
445 447 auth_token):
446 448 whitelist = self._get_api_whitelist([])
447 449 with mock.patch.dict('rhodecode.CONFIG', whitelist):
448 450 assert [] == whitelist['api_access_controllers_whitelist']
449 451 if test_name == 'proper_auth_token':
450 452 # use builtin if api_key is None
451 453 auth_token = User.get_first_super_admin().api_key
452 454
453 455 with fixture.anon_access(False):
454 456 self.app.get(url(controller='changeset',
455 457 action='changeset_raw',
456 458 repo_name=HG_REPO, revision='tip',
457 459 api_key=auth_token),
458 460 status=302)
459 461
460 462 @pytest.mark.parametrize("test_name, auth_token, code", [
461 463 ('none', None, 302),
462 464 ('empty_string', '', 302),
463 465 ('fake_number', '123456', 302),
464 466 ('proper_auth_token', None, 200)
465 467 ])
466 468 def test_access_whitelisted_page_via_auth_token(self, test_name,
467 469 auth_token, code):
468 470 whitelist = self._get_api_whitelist(
469 471 ['ChangesetController:changeset_raw'])
470 472 with mock.patch.dict('rhodecode.CONFIG', whitelist):
471 473 assert ['ChangesetController:changeset_raw'] == \
472 474 whitelist['api_access_controllers_whitelist']
473 475 if test_name == 'proper_auth_token':
474 476 auth_token = User.get_first_super_admin().api_key
475 477
476 478 with fixture.anon_access(False):
477 479 self.app.get(url(controller='changeset',
478 480 action='changeset_raw',
479 481 repo_name=HG_REPO, revision='tip',
480 482 api_key=auth_token),
481 483 status=code)
482 484
483 485 def test_access_page_via_extra_auth_token(self):
484 486 whitelist = self._get_api_whitelist(
485 487 ['ChangesetController:changeset_raw'])
486 488 with mock.patch.dict('rhodecode.CONFIG', whitelist):
487 489 assert ['ChangesetController:changeset_raw'] == \
488 490 whitelist['api_access_controllers_whitelist']
489 491
490 492 new_auth_token = AuthTokenModel().create(
491 493 TEST_USER_ADMIN_LOGIN, 'test')
492 494 Session().commit()
493 495 with fixture.anon_access(False):
494 496 self.app.get(url(controller='changeset',
495 497 action='changeset_raw',
496 498 repo_name=HG_REPO, revision='tip',
497 499 api_key=new_auth_token.api_key),
498 500 status=200)
499 501
500 502 def test_access_page_via_expired_auth_token(self):
501 503 whitelist = self._get_api_whitelist(
502 504 ['ChangesetController:changeset_raw'])
503 505 with mock.patch.dict('rhodecode.CONFIG', whitelist):
504 506 assert ['ChangesetController:changeset_raw'] == \
505 507 whitelist['api_access_controllers_whitelist']
506 508
507 509 new_auth_token = AuthTokenModel().create(
508 510 TEST_USER_ADMIN_LOGIN, 'test')
509 511 Session().commit()
510 512 # patch the api key and make it expired
511 513 new_auth_token.expires = 0
512 514 Session().add(new_auth_token)
513 515 Session().commit()
514 516 with fixture.anon_access(False):
515 517 self.app.get(url(controller='changeset',
516 518 action='changeset_raw',
517 519 repo_name=HG_REPO, revision='tip',
518 520 api_key=new_auth_token.api_key),
519 521 status=302)
522
523
524 class TestPasswordReset(TestController):
525
526 @pytest.mark.parametrize(
527 'pwd_reset_setting, show_link, show_reset', [
528 ('hg.password_reset.enabled', True, True),
529 ('hg.password_reset.hidden', False, True),
530 ('hg.password_reset.disabled', False, False),
531 ])
532 def test_password_reset_settings(
533 self, pwd_reset_setting, show_link, show_reset):
534 clear_all_caches()
535 self.log_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
536 params = {
537 'csrf_token': self.csrf_token,
538 'anonymous': 'True',
539 'default_register': 'hg.register.auto_activate',
540 'default_register_message': '',
541 'default_password_reset': pwd_reset_setting,
542 'default_extern_activate': 'hg.extern_activate.auto',
543 }
544 resp = self.app.post(url('admin_permissions_application'), params=params)
545 self.logout_user()
546
547 login_page = self.app.get(login_url)
548 asr_login = AssertResponse(login_page)
549 index_page = self.app.get(index_url)
550 asr_index = AssertResponse(index_page)
551
552 if show_link:
553 asr_login.one_element_exists('a.pwd_reset')
554 asr_index.one_element_exists('a.pwd_reset')
555 else:
556 asr_login.no_element_exists('a.pwd_reset')
557 asr_index.no_element_exists('a.pwd_reset')
558
559 pwdreset_page = self.app.get(pwd_reset_url)
560
561 asr_reset = AssertResponse(pwdreset_page)
562 if show_reset:
563 assert 'Send password reset email' in pwdreset_page
564 asr_reset.one_element_exists('#email')
565 asr_reset.one_element_exists('#send')
566 else:
567 assert 'Password reset has been disabled.' in pwdreset_page
568 asr_reset.no_element_exists('#email')
569 asr_reset.no_element_exists('#send')
570
571 def test_password_form_disabled(self):
572 self.log_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
573 params = {
574 'csrf_token': self.csrf_token,
575 'anonymous': 'True',
576 'default_register': 'hg.register.auto_activate',
577 'default_register_message': '',
578 'default_password_reset': 'hg.password_reset.disabled',
579 'default_extern_activate': 'hg.extern_activate.auto',
580 }
581 self.app.post(url('admin_permissions_application'), params=params)
582 self.logout_user()
583
584 pwdreset_page = self.app.post(
585 pwd_reset_url,
586 {'email': 'lisa@rhodecode.com',}
587 )
588 assert 'Password reset has been disabled.' in pwdreset_page
General Comments 0
You need to be logged in to leave comments. Login now