Commit d34cc66f authored by hxquangnhat's avatar hxquangnhat

add login page, support cookie

parent 1ef8cc5a
......@@ -32,7 +32,7 @@ from zoe_api.web.custom_request_handler import JinjaApp
log = logging.getLogger("zoe_api")
LOG_FORMAT = '%(asctime)-15s %(levelname)s %(threadName)s->%(name)s: %(message)s'
COOKIE_SECRET = "bZJc2sWbQLKos6GkHn/VB9oXwQt8S0R0kRvJ5/xJ89E="
def zoe_web_main() -> int:
"""
......@@ -57,12 +57,13 @@ def zoe_web_main() -> int:
app_settings = {
'static_path': os.path.join(os.path.dirname(__file__), "web", "static"),
'template_path': os.path.join(os.path.dirname(__file__), "web", "templates"),
# 'debug': args.debug
'cookie_secret': COOKIE_SECRET,
'debug': args.debug
}
# Remove the old FE entry point
#app = Application(zoe_api.web.web_init(api_endpoint) + zoe_api.rest_api.api_init(api_endpoint), **app_settings)
app = Application(zoe_api.rest_api.api_init(api_endpoint), **app_settings)
app = Application(zoe_api.web.web_init(api_endpoint) + zoe_api.rest_api.api_init(api_endpoint), **app_settings)
#app = Application(zoe_api.rest_api.api_init(api_endpoint), **app_settings)
JinjaApp.init_app(app)
log.info("Starting HTTP server...")
......
......@@ -26,6 +26,7 @@ from zoe_api.rest_api.service import ServiceAPI, ServiceLogsAPI
from zoe_api.rest_api.discovery import DiscoveryAPI
from zoe_api.rest_api.statistics import SchedulerStatsAPI
from zoe_api.rest_api.oauth import OAuthGetAPI, OAuthRevokeAPI
from zoe_api.rest_api.login import LoginAPI
from zoe_lib.version import ZOE_API_VERSION
......@@ -41,6 +42,7 @@ def api_init(api_endpoint) -> List[tornado.web.URLSpec]:
api_routes = [
tornado.web.url(API_PATH + r'/info', InfoAPI, route_args),
tornado.web.url(API_PATH + r'/userinfo', UserInfoAPI, route_args),
tornado.web.url(API_PATH + r'/login', LoginAPI, route_args),
tornado.web.url(API_PATH + r'/execution/([0-9]+)', ExecutionAPI, route_args),
tornado.web.url(API_PATH + r'/execution/delete/([0-9]+)', ExecutionDeleteAPI, route_args),
......
# Copyright (c) 2016, Daniele Venzano
#
# Licensed 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.
"""The Info API endpoint."""
from tornado.web import RequestHandler
from zoe_api.rest_api.utils import get_auth, catch_exceptions
class LoginAPI(RequestHandler):
"""The Login API endpoint."""
def initialize(self, **kwargs):
"""Initializes the request handler."""
self.api_endpoint = kwargs['api_endpoint'] # type: APIEndpoint
@catch_exceptions
def get(self):
"""HTTP GET method."""
uid, role = get_auth(self)
cookieVal = uid + '.' + role
self.set_secure_cookie('zoe', cookieVal)
ret = {
'uid': uid,
'role': role
}
self.write(ret)
def data_received(self, chunk):
"""Not implemented as we do not use stream uploads"""
pass
......@@ -72,6 +72,12 @@ def catch_exceptions(func):
def get_auth(handler: tornado.web.RequestHandler):
"""Try to authenticate a request."""
if handler.get_secure_cookie('zoe'):
cookie_val = str(handler.get_secure_cookie('zoe'))
uid, role = cookie_val[2:-1].split('.')
log.info('Authentication done using cookie')
return uid, role
auth_header = handler.request.headers.get('Authorization')
if auth_header is None or not (auth_header.startswith('Basic ') or auth_header.startswith('Bearer ')):
raise ZoeRestAPIException('missing or wrong authentication information', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'})
......@@ -110,5 +116,6 @@ def get_auth(handler: tornado.web.RequestHandler):
uid, role = authenticator.auth(username, password)
if uid is None:
raise ZoeRestAPIException('missing or wrong authentication information', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'})
log.info('Authentication done using auth-mechanism')
return uid, role
......@@ -38,6 +38,7 @@ def web_init(api_endpoint) -> List[tornado.web.URLSpec]:
web_routes = [
tornado.web.url(r'/', zoe_api.web.start.RootWeb, route_args, name='root'),
tornado.web.url(r'/user', zoe_api.web.start.HomeWeb, route_args, name='home_user'),
tornado.web.url(r'/login', zoe_api.web.start.LoginWeb, route_args, name='login'),
tornado.web.url(r'/executions/new', zoe_api.web.executions.ExecutionDefineWeb, route_args, name='execution_define'),
tornado.web.url(r'/executions/start', zoe_api.web.executions.ExecutionStartWeb, route_args, name='execution_start'),
......
......@@ -19,10 +19,9 @@ from random import randint
import json
from zoe_api.api_endpoint import APIEndpoint # pylint: disable=unused-import
from zoe_api.web.utils import get_auth, catch_exceptions
from zoe_api.web.utils import get_auth, get_auth_login, catch_exceptions
from zoe_api.web.custom_request_handler import ZoeRequestHandler
class RootWeb(ZoeRequestHandler):
"""Handler class"""
def initialize(self, **kwargs):
......@@ -35,6 +34,26 @@ class RootWeb(ZoeRequestHandler):
"""Home page without authentication."""
self.render('index.html')
class LoginWeb(ZoeRequestHandler):
def initialize(self, **kwargs):
"""Initializes the request handler."""
super().initialize(**kwargs)
self.api_endpoint = kwargs['api_endpoint'] # type: APIEndpoint
@catch_exceptions
def get(self):
self.render('login.html')
@catch_exceptions
def post(self):
username = self.get_argument("username", "")
password = self.get_argument("password", "")
uid, role = get_auth_login(username, password)
if not self.get_secure_cookie('zoe'):
cookieVal = uid + '.' + role
self.set_secure_cookie('zoe',cookieVal)
self.redirect(self.get_argument("next", u"/user"))
class HomeWeb(ZoeRequestHandler):
"""Handler class"""
......
<div id="main-container">
<div id="main">
<h1>
<img alt="zoe dummy login page" src="{{ static_url("img/logo.png") }}">
</h1>
<div id="login-form">
<form action="/login" method="post" id="login_form">
<fieldset>
<label for="username">Username</label>
<input autocapitalize="off" autocorrect="off" class="text-input" id="username" name="username" tabindex="1" type="text" value="">
</fieldset>
<fieldset>
<label for="password">Password</label>
<input class="text-input" id="password" name="password" tabindex="2" type="password" value="">
</fieldset>
<fieldset>
<span class="errormessage">{{errormessage}}</span>
</fieldset>
<div id="form_btn">
<input id="signin-btn" class="btn btn-blue" type="submit" value="Sign In" tabindex="3">
</div>
</form>
</div>
</div>
</div>
......@@ -22,6 +22,7 @@ from zoe_lib.config import get_conf
from zoe_api.auth.base import BaseAuthenticator # pylint: disable=unused-import
from zoe_api.auth.ldap import LDAPAuthenticator
from zoe_api.auth.ldapsasl import LDAPSASLAuthenticator
from zoe_api.auth.file import PlainTextAuthenticator
import zoe_api.exceptions
from zoe_api.web.custom_request_handler import ZoeRequestHandler
......@@ -52,17 +53,38 @@ def catch_exceptions(func):
return func_wrapper
def missing_auth(handler: ZoeRequestHandler):
"""Sends a 401 response that enables basic auth"""
handler.set_status(401, 'Could not verify your access level for that URL. You have to login with proper credentials.')
handler.set_header('WWW-Authenticate', 'Basic realm="Login Required"')
handler.finish()
if handler.get_argument('username') is not None:
handler.redirect(handler.get_argument('next', u'login'))
else:
"""Sends a 401 response that enables basic auth"""
handler.set_status(401, 'Could not verify your access level for that URL. You have to login with proper credentials.')
handler.set_header('WWW-Authenticate', 'Basic realm="Login Required"')
handler.finish()
def get_auth_login(username, password):
if get_conf().auth_type == 'text':
authenticator = PlainTextAuthenticator() # type: BaseAuthenticator
elif get_conf().auth_type == 'ldap':
authenticator = LDAPAuthenticator()
elif get_conf().auth_type == 'ldapsasl':
authenticator = LDAPSASLAuthenticator()
else:
raise zoe_api.exceptions.ZoeException('Configuration error, unknown authentication method: {}'.format(get_conf().auth_type))
uid, role = authenticator.auth(username, password)
if uid is None:
raise zoe_api.exceptions.ZoeAuthException
return uid, role
def get_auth(handler: ZoeRequestHandler):
"""Try to authenticate a request."""
if handler.get_secure_cookie('zoe'):
cookie_val = str(handler.get_secure_cookie('zoe'))
uid, role = cookie_val[2:-1].split('.')
log.info('Authentication done using cookie')
return uid, role
auth_header = handler.request.headers.get('Authorization')
if auth_header is None or not auth_header.startswith('Basic '):
raise zoe_api.exceptions.ZoeAuthException
......@@ -79,7 +101,7 @@ def get_auth(handler: ZoeRequestHandler):
uid, role = authenticator.auth(username, password)
if uid is None:
raise zoe_api.exceptions.ZoeAuthException
log.info('Authentication done using auth-mechanism')
return uid, role
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment