瀏覽代碼

[notebook] Show modal login dialog in case the session is expired

Enrico Berti 10 年之前
父節點
當前提交
1c568cc

+ 14 - 3
desktop/core/src/desktop/auth/views.py

@@ -41,6 +41,7 @@ from useradmin.views import ensure_home_directory, require_change_password
 from desktop.auth import forms as auth_forms
 from desktop.lib.django_util import render
 from desktop.lib.django_util import login_notrequired
+from desktop.lib.django_util import JsonResponse
 from desktop.log.access import access_warn, last_access_map
 from desktop.conf import AUTH, LDAP, OAUTH, DEMO_ENABLED
 
@@ -83,7 +84,7 @@ def get_backend_names():
 
 @login_notrequired
 @watch_login
-def dt_login(request):
+def dt_login(request, from_modal=False):
   redirect_to = request.REQUEST.get('next', '/')
   is_first_login_ever = first_login_ever()
   backend_names = get_backend_names()
@@ -137,12 +138,17 @@ def dt_login(request):
         msg = 'Successful login for user: %s' % user.username
         request.audit['operationText'] = msg
         access_warn(request, msg)
-        return HttpResponseRedirect(redirect_to)
+        if from_modal or request.REQUEST.get('fromModal', 'false') == 'true':
+          return JsonResponse({'auth': True})
+        else:
+          return HttpResponseRedirect(redirect_to)
       else:
         request.audit['allowed'] = False
         msg = 'Failed login for user: %s' % request.POST.get('username')
         request.audit['operationText'] = msg
         access_warn(request, msg)
+        if from_modal or request.REQUEST.get('fromModal', 'false') == 'true':
+          return JsonResponse({'auth': False})
 
   else:
     first_user_form = None
@@ -155,7 +161,12 @@ def dt_login(request):
     return HttpResponseRedirect(redirect_to)
 
   request.session.set_test_cookie()
-  return render('login.mako', request, {
+
+  renderable_path = 'login.mako'
+  if from_modal:
+    renderable_path = 'login_modal.mako'
+
+  return render(renderable_path, request, {
     'action': urlresolvers.reverse('desktop.auth.views.dt_login'),
     'form': first_user_form or auth_form,
     'next': redirect_to,

+ 172 - 0
desktop/core/src/desktop/static/desktop/css/login.css

@@ -0,0 +1,172 @@
+/*
+ Licensed to Cloudera, Inc. under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  Cloudera, Inc. licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+@-webkit-keyframes spinner {
+  from {
+    -webkit-transform: rotateY(0deg);
+  }
+  to {
+    -webkit-transform: rotateY(-360deg);
+  }
+}
+
+.login-box .logo {
+  background-color: #338BB8;
+  width: 60px;
+  height: 60px;
+  text-align: center;
+  -webkit-border-radius: 30px;
+  -moz-border-radius: 30px;
+  border-radius: 30px;
+  margin-left: 400px;
+  margin-top: -30px;
+}
+
+.login-box .logo img {
+  margin-top: 2px;
+}
+
+.login-box .logo img.waiting {
+  -webkit-animation-name: spinner;
+  -webkit-animation-timing-function: linear;
+  -webkit-animation-iteration-count: infinite;
+  -webkit-animation-duration: 2s;
+  -webkit-transform-style: preserve-3d;
+}
+
+.login-box {
+  width: 500px;
+  display: block;
+  margin: auto;
+  margin-bottom: 50px;
+  background: #fff;
+  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
+}
+
+.login-box .input-prepend.error input, .login-box .input-prepend.error .add-on {
+  border-top-color: #dd4b39;
+  border-bottom-color: #dd4b39;
+}
+
+.login-box .input-prepend.error input {
+  border-right-color: #dd4b39;
+}
+
+.login-box .input-prepend.error .add-on {
+  border-left-color: #dd4b39;
+}
+
+.login-box input[type='submit'] {
+  height: 44px;
+  min-height: 44px;
+  font-weight: normal;
+  text-shadow: none;
+}
+
+.login-header {
+  background-color: #fafafa !important;
+  padding: 35px;
+}
+
+.login-header h1 {
+  margin: 0;
+  font-size: 20px;
+  line-height: 20px;
+}
+
+.login-header h2 {
+  margin: 5px 0 0 0;
+  color: #757575;
+  font-weight: normal;
+  font-size: 16px;
+  line-height: 16px;
+}
+
+.login-content {
+  padding: 35px;
+}
+
+.login-box ul.errorlist {
+  text-align: left;
+  margin-bottom: 4px;
+  margin-top: -4px;
+}
+
+.login-box .alert-error ul.errorlist {
+  text-align: center;
+  margin-top: 0;
+}
+
+.login-box ul.errorlist li {
+  font-size: 13px;
+  font-weight: normal;
+  font-style: normal;
+}
+
+.login-content input[type='text'], .login-content input[type='password'], .login-content input[type='text']:hover, .login-content input[type='password']:hover {
+  padding: 0 10px 2px 0;
+  height: auto;
+  border: none;
+  border-bottom: 2px solid #eeeeee;
+  border-radius: 0;
+  background: none;
+  box-shadow: none;
+  color: #9e9e9e;
+  font-weight: 400;
+  resize: none;
+  width: 100%;
+}
+
+.login-box input:-webkit-autofill {
+  -webkit-box-shadow: 0 0 0px 1000px white inset !important;
+}
+
+.login-content input.error {
+  border-color: #b94a48;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+  -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.login-box .btn-large.btn-primary {
+  width: 100%;
+  margin-top: 20px;
+}
+
+.login-box .chosen-single {
+  min-height: 38px;
+  text-align: left;
+  font-size: 18px;
+}
+
+.login-box .chosen-single span {
+  display: inline;
+  line-height: 38px;
+  vertical-align: middle;
+}
+
+.login-box .chosen-container-active.chosen-with-drop .chosen-single div b,
+.login-box .chosen-container-single .chosen-single div b {
+  background-position-x: 1px;
+  background-position-y: 10px;
+}
+
+.login-box .chosen-container-active.chosen-with-drop .chosen-single div b {
+  background-position-x: -17px;
+  background-position-y: 10px;
+}

+ 2 - 160
desktop/core/src/desktop/templates/login.mako

@@ -21,169 +21,15 @@
   from useradmin.password_policy import is_password_policy_enabled, get_password_hint
 %>
 
-${ commonheader("Welcome to Hue", "login", user, "50px", True) | n,unicode }
+${ commonheader(_("Welcome to Hue"), "login", user, "50px", True) | n,unicode }
 
+<link rel="stylesheet" href="${ static('desktop/css/login.css') }">
 <link rel="stylesheet" href="${ static('desktop/ext/chosen/chosen.min.css') }">
 <style type="text/css">
   body {
     background-color: #EEE;
     padding-top: 150px;
   }
-
-  @-webkit-keyframes spinner {
-    from {
-      -webkit-transform: rotateY(0deg);
-    }
-    to {
-      -webkit-transform: rotateY(-360deg);
-    }
-  }
-
-  .logo {
-    background-color: #338BB8;
-    width: 60px;
-    height: 60px;
-    text-align: center;
-    -webkit-border-radius: 30px;
-    -moz-border-radius: 30px;
-    border-radius: 30px;
-    margin-left: 400px;
-    margin-top: -30px;
-  }
-
-  .logo img {
-    margin-top: 2px;
-  }
-
-  .logo img.waiting {
-    -webkit-animation-name: spinner;
-    -webkit-animation-timing-function: linear;
-    -webkit-animation-iteration-count: infinite;
-    -webkit-animation-duration: 2s;
-    -webkit-transform-style: preserve-3d;
-  }
-
-  .login-box {
-    width: 500px;
-    display: block;
-    margin: auto;
-    margin-bottom: 50px;
-    background: #fff;
-    box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
-  }
-
-  .login-box .input-prepend.error input, .login-box .input-prepend.error .add-on {
-    border-top-color: #dd4b39;
-    border-bottom-color: #dd4b39;
-  }
-
-  .login-box .input-prepend.error input {
-    border-right-color: #dd4b39;
-  }
-
-  .login-box .input-prepend.error .add-on {
-    border-left-color: #dd4b39;
-  }
-
-  .login-box input[type='submit'] {
-    height: 44px;
-    min-height: 44px;
-    font-weight: normal;
-    text-shadow: none;
-  }
-
-  .login-header {
-    background-color: #fafafa !important;
-    padding: 35px;
-  }
-
-  .login-header h1 {
-    margin: 0;
-    font-size: 18px;
-    line-height: 18px;
-  }
-
-  .login-header h2 {
-    margin: 5px 0 0 0;
-    color: #757575;
-    font-weight: normal;
-    font-size: 14px;
-    line-height: 14px;
-  }
-
-  .login-content {
-    padding: 35px;
-  }
-
-  ul.errorlist {
-    text-align: left;
-    margin-bottom: 4px;
-    margin-top: -4px;
-  }
-
-  .alert-error ul.errorlist {
-    text-align: center;
-    margin-top: 0;
-  }
-
-  ul.errorlist li {
-    font-size: 13px;
-    font-weight: normal;
-    font-style: normal;
-  }
-
-  .login-content input[type='text'], .login-content input[type='password'], .login-content input[type='text']:hover, .login-content input[type='password']:hover {
-    padding: 0 10px 2px 0;
-    height: auto;
-    border: none;
-    border-bottom: 2px solid #eeeeee;
-    border-radius: 0;
-    background: none;
-    box-shadow: none;
-    color: #9e9e9e;
-    font-weight: 400;
-    resize: none;
-    width: 100%;
-  }
-
-  input:-webkit-autofill {
-    -webkit-box-shadow: 0 0 0px 1000px white inset!important;
-  }
-
-  .login-content input.error {
-    border-color: #b94a48;
-    -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-    -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-  }
-
-  .btn-large.btn-primary {
-    width: 100%;
-    margin-top: 20px;
-  }
-
-  .chosen-single {
-    min-height: 38px;
-    text-align: left;
-    font-size: 18px;
-  }
-
-  .chosen-single span {
-    display: inline;
-    line-height: 38px;
-    vertical-align: middle;
-  }
-
-  .chosen-container-active.chosen-with-drop .chosen-single div b,
-  .chosen-container-single .chosen-single div b {
-    background-position-x: 1px;
-    background-position-y: 10px;
-  }
-
-  .chosen-container-active.chosen-with-drop .chosen-single div b {
-    background-position-x: -17px;
-    background-position-y: 10px;
-  }
 </style>
 
 
@@ -303,10 +149,6 @@ ${ commonheader("Welcome to Hue", "login", user, "50px", True) | n,unicode }
         return false;
       });
     %endif
-
-    $("ul.errorlist").each(function () {
-      $(this).prev().addClass("error");
-    });
   });
 </script>
 

+ 122 - 0
desktop/core/src/desktop/templates/login_modal.mako

@@ -0,0 +1,122 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.    See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##       http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+<%!
+  from desktop import conf
+  from django.utils.translation import ugettext as _
+  from useradmin.password_policy import is_password_policy_enabled, get_password_hint
+%>
+
+
+<link rel="stylesheet" href="${ static('desktop/css/login.css') }">
+<link rel="stylesheet" href="${ static('desktop/ext/chosen/chosen.min.css') }">
+
+<div id="login-modal" class="modal fade hide" data-backdrop="true" style="padding: 0px!important;box-shadow: none;background: transparent;">
+  <div class="login-box">
+    <form method="POST" action="${action}">
+    <input type="hidden" name="csrfmiddlewaretoken" value="">
+    <div class="login-header">
+      <h1>${ _('You have been logged out') }</h1>
+      <h2>${_('Sign in to continue to use Hue')}</h2>
+    </div>
+
+    <div class="logo"><img src="${ static('desktop/art/hue-login-white.png') }" width="50" height="50" /> </div>
+
+    <div class="login-content">
+
+      <div class="
+        %if backend_names == ['OAuthBackend']:
+          hide
+        %endif
+      ">
+        ${ form['username'] | n,unicode }
+      </div>
+
+      ${ form['username'].errors | n,unicode }
+
+      <div class="
+        %if 'AllowAllBackend' in backend_names or backend_names == ['OAuthBackend']:
+          hide
+        %endif
+      ">
+        ${ form['password'] | n,unicode }
+      </div>
+
+      ${ form['password'].errors | n,unicode }
+
+      %if active_directory:
+      <div>
+        ${ form['server'] | n,unicode }
+      </div>
+      %endif
+
+      <div class="login-error alert alert-error hide" style="text-align: center">
+        <strong><i class="fa fa-exclamation-triangle"></i> ${_('Sign in failed. Please try again.')}</strong>
+      </div>
+
+      <input type="submit" class="btn btn-large btn-primary" value="${_('Sign in')}"/>
+      <input type="hidden" name="next" value="${next}"/>
+      <input type="hidden" name="fromModal" value="true"/>
+      </div>
+
+    </form>
+
+    %if conf.CUSTOM.LOGIN_SPLASH_HTML.get():
+    <div class="alert alert-info" id="login-splash">
+      ${ conf.CUSTOM.LOGIN_SPLASH_HTML.get() | n,unicode }
+    </div>
+    %endif
+  </div>
+</div>
+
+<script src="${ static('desktop/ext/chosen/chosen.jquery.min.js') }" type="text/javascript" charset="utf-8"></script>
+<script>
+  $(document).ready(function () {
+    $('#id_server').chosen({
+      disable_search_threshold: 5,
+      width: "90%",
+      no_results_text: "${_('Oops, no database found!')}"
+    });
+
+    $('#login-modal form').on('submit', function () {
+      $('input[name="csrfmiddlewaretoken"]').val($.cookie('csrftoken'));
+      $.ajax({
+        type: 'POST',
+        url: $(this).attr('action'),
+        data: $(this).serialize(),
+        success: function (response) {
+          $('#login-modal .logo').find('img').removeClass('waiting');
+          huePubSub.publish('hue.login.result', response);
+        }
+      });
+      $('#login-modal .logo').find('img').addClass('waiting');
+      return false;
+    });
+
+    %if 'AllowAllBackend' in backend_names:
+      $('#id_password').val('password');
+    %endif
+
+    %if backend_names == ['OAuthBackend']:
+      $('#login-modal input').css({'display': 'block', 'margin-left': 'auto', 'margin-right': 'auto'});
+      $('#login-modal input').bind('click', function () {
+        window.location.replace('/login/oauth/');
+        return false;
+      });
+    %endif
+  });
+</script>
+

+ 3 - 0
desktop/core/src/desktop/views.py

@@ -399,6 +399,9 @@ def commonshare():
 def commonimportexport(request):
   return django_mako.render_to_string("common_import_export.mako", {'request': request})
 
+def login_modal(request):
+  return desktop.auth.views.dt_login(request, True)
+
 def commonfooter(messages=None):
   """
   Returns the rendered common footer

+ 17 - 16
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -18,7 +18,7 @@
 from desktop import conf
 from desktop.lib.i18n import smart_unicode
 from django.utils.translation import ugettext as _
-from desktop.views import _ko
+from desktop.views import _ko, login_modal
 %>
 
 <%namespace name="require" file="/require.mako" />
@@ -265,18 +265,8 @@ ${ require.config() }
 
 <%def name="commonHTML()">
 
-<div id="loginRequiredModal" class="modal hide" data-backdrop="true">
-  <div class="modal-header">
-    <a href="javascript: void(0)" data-dismiss="modal" class="pull-right"><i class="fa fa-times"></i></a>
-    <h3>${_('You have been logged off')}</h3>
-  </div>
-  <div class="modal-body">
-    ${ _('To continue without losing your work, please') } <a href="/accounts/login/" target="_blank">${ _('sign in on another tab') }</a> ${ _('and then return to this editor.')}
-  </div>
-  <div class="modal-footer">
-    <a href="#" class="btn" data-dismiss="modal">${_('Close')}</a>
-  </div>
-</div>
+
+${ login_modal(request).content | n,unicode }
 
 
 <div id="combinedContentModal" class="modal hide" data-backdrop="true" style="width:780px;margin-left:-410px!important">
@@ -1970,17 +1960,28 @@ ${ require.config() }
       $(document).ajaxSuccess(function (event, xhr, settings, data) {
         if (data === '/* login required */' && !isLoginRequired) {
           isLoginRequired = true;
-          $('#loginRequiredModal').modal('show');
-          window.setTimeout(function(){
+          $('#login-modal').modal('show');
+          window.setTimeout(function () {
             $('.jHueNotify').remove();
           }, 200);
         }
       });
 
-      $('#loginRequiredModal').on('hidden', function(){
+      $('#login-modal').on('hidden', function () {
         isLoginRequired = false;
       });
 
+      huePubSub.subscribe('hue.login.result', function (response) {
+        if (response.auth) {
+          $('#login-modal').modal('hide');
+          $.jHueNotify.info('${ _('You have signed in successfully!') }');
+          $('#login-modal .login-error').addClass('hide');
+        }
+        else {
+          $('#login-modal .login-error').removeClass('hide');
+        }
+      });
+
 
       if (location.getParameter("github_status") != "") {
         if (location.getParameter("github_status") == "0") {