commit
6cf7a7bffa
1
Pipfile
1
Pipfile
@ -7,7 +7,6 @@ name = "pypi"
|
|||||||
tornado = "==5.0.2"
|
tornado = "==5.0.2"
|
||||||
webassets = "==0.12.1"
|
webassets = "==0.12.1"
|
||||||
Unipath = "==1.1"
|
Unipath = "==1.1"
|
||||||
requests = "*"
|
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
pytest = "==3.6.0"
|
pytest = "==3.6.0"
|
||||||
|
38
Pipfile.lock
generated
38
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "68a0d5093979093899f0f86faa82eb55f90f9a67a16b11a5701ea85096e72ee8"
|
"sha256": "391e254ddb902877afca9c07aa2306710ce6d1e207b029c1a8b5dc0115ee99a5"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -16,35 +16,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"certifi": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7",
|
|
||||||
"sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0"
|
|
||||||
],
|
|
||||||
"version": "==2018.4.16"
|
|
||||||
},
|
|
||||||
"chardet": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
|
||||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
|
||||||
],
|
|
||||||
"version": "==3.0.4"
|
|
||||||
},
|
|
||||||
"idna": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f",
|
|
||||||
"sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4"
|
|
||||||
],
|
|
||||||
"version": "==2.6"
|
|
||||||
},
|
|
||||||
"requests": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b",
|
|
||||||
"sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.18.4"
|
|
||||||
},
|
|
||||||
"tornado": {
|
"tornado": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:1b83d5c10550f2653380b4c77331d6f8850f287c4f67d7ce1e1c639d9222fbc7",
|
"sha256:1b83d5c10550f2653380b4c77331d6f8850f287c4f67d7ce1e1c639d9222fbc7",
|
||||||
@ -64,13 +35,6 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.1"
|
"version": "==1.1"
|
||||||
},
|
},
|
||||||
"urllib3": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b",
|
|
||||||
"sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"
|
|
||||||
],
|
|
||||||
"version": "==1.22"
|
|
||||||
},
|
|
||||||
"webassets": {
|
"webassets": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:e7d9c8887343123fd5b32309b33167428cb1318cdda97ece12d0907fd69d38db"
|
"sha256:e7d9c8887343123fd5b32309b33167428cb1318cdda97ece12d0907fd69d38db"
|
||||||
|
@ -33,7 +33,7 @@ class ApiClient(object):
|
|||||||
kwargs['body'] = dumps(kwargs['json'])
|
kwargs['body'] = dumps(kwargs['json'])
|
||||||
del kwargs['json']
|
del kwargs['json']
|
||||||
headers = kwargs.get('headers', {})
|
headers = kwargs.get('headers', {})
|
||||||
headers['Content-Type'] = 'application-json'
|
headers['Content-Type'] = 'application/json'
|
||||||
kwargs['headers'] = headers
|
kwargs['headers'] = headers
|
||||||
|
|
||||||
response = yield self.client.fetch(url, method=method, **kwargs)
|
response = yield self.client.fetch(url, method=method, **kwargs)
|
||||||
|
62
atst/app.py
62
atst/app.py
@ -4,34 +4,62 @@ import tornado.web
|
|||||||
from tornado.web import url
|
from tornado.web import url
|
||||||
|
|
||||||
from atst.handlers.main import MainHandler
|
from atst.handlers.main import MainHandler
|
||||||
|
from atst.handlers.home import Home
|
||||||
|
from atst.handlers.login import Login
|
||||||
from atst.handlers.workspace import Workspace
|
from atst.handlers.workspace import Workspace
|
||||||
from atst.handlers.request import Request
|
from atst.handlers.request import Request
|
||||||
from atst.handlers.request_new import RequestNew
|
from atst.handlers.request_new import RequestNew
|
||||||
|
from atst.handlers.dev import Dev
|
||||||
from atst.home import home
|
from atst.home import home
|
||||||
from atst.api_client import ApiClient
|
from atst.api_client import ApiClient
|
||||||
|
|
||||||
|
ENV = os.getenv("TORNADO_ENV", "dev")
|
||||||
|
|
||||||
|
|
||||||
def make_app(config):
|
def make_app(config):
|
||||||
|
|
||||||
authz_client = ApiClient(config['default']['AUTHZ_BASE_URL'])
|
authz_client = ApiClient(config["default"]["AUTHZ_BASE_URL"])
|
||||||
|
authnid_client = ApiClient(config["default"]["AUTHNID_BASE_URL"])
|
||||||
|
|
||||||
app = tornado.web.Application([
|
routes = [
|
||||||
url( r"/", MainHandler, {'page': 'login'}, name='login' ),
|
url(r"/", Home, {"page": "login"}, name="main"),
|
||||||
url( r"/home", MainHandler, {'page': 'home'}, name='home' ),
|
url(
|
||||||
|
r"/login",
|
||||||
|
Login,
|
||||||
|
{"authnid_client": authnid_client},
|
||||||
|
name="login",
|
||||||
|
),
|
||||||
|
url(r"/home", MainHandler, {"page": "home"}, name="home"),
|
||||||
url( r"/workspaces/blank", MainHandler, {'page': 'workspaces_blank'}, name='workspaces_blank' ),
|
url( r"/workspaces/blank", MainHandler, {'page': 'workspaces_blank'}, name='workspaces_blank' ),
|
||||||
url( r"/workspaces",
|
url(
|
||||||
|
r"/workspaces",
|
||||||
Workspace,
|
Workspace,
|
||||||
{'page': 'workspaces', 'authz_client': authz_client},
|
{"page": "workspaces", "authz_client": authz_client},
|
||||||
name='workspaces'),
|
name="workspaces",
|
||||||
url( r"/requests", Request, {'page': 'requests'}, name='requests' ),
|
),
|
||||||
url( r"/requests/new", RequestNew, {'page': 'requests_new'}, name='request_new' ),
|
url(r"/requests", Request, {"page": "requests"}, name="requests"),
|
||||||
url( r"/requests/new/([0-9])", RequestNew, {'page': 'requests_new'}, name='request_form' ),
|
url(r"/requests/new", RequestNew, {"page": "requests_new"}, name="request_new"),
|
||||||
url( r"/users", MainHandler, {'page': 'users'}, name='users' ),
|
url(
|
||||||
url( r"/reports", MainHandler, {'page': 'reports'}, name='reports' ),
|
r"/requests/new/([0-9])",
|
||||||
url( r"/calculator", MainHandler, {'page': 'calculator'}, name='calculator' ),
|
RequestNew,
|
||||||
],
|
{"page": "requests_new"},
|
||||||
|
name="request_form",
|
||||||
|
),
|
||||||
|
url(r"/users", MainHandler, {"page": "users"}, name="users"),
|
||||||
|
url(r"/reports", MainHandler, {"page": "reports"}, name="reports"),
|
||||||
|
url(r"/calculator", MainHandler, {"page": "calculator"}, name="calculator"),
|
||||||
|
]
|
||||||
|
|
||||||
|
if not ENV == "production":
|
||||||
|
routes += [url(r"/login-dev", Dev, {"action": "login"}, name="dev-login")]
|
||||||
|
|
||||||
|
app = tornado.web.Application(
|
||||||
|
routes,
|
||||||
|
login_url="/",
|
||||||
|
|
||||||
template_path = home.child('templates'),
|
template_path = home.child('templates'),
|
||||||
static_path = home.child('static'),
|
static_path = home.child('static'),
|
||||||
|
cookie_secret=config["default"]["COOKIE_SECRET"],
|
||||||
debug=config['default'].getboolean('DEBUG')
|
debug=config['default'].getboolean('DEBUG')
|
||||||
)
|
)
|
||||||
return app
|
return app
|
||||||
@ -40,12 +68,12 @@ def make_app(config):
|
|||||||
def make_config():
|
def make_config():
|
||||||
BASE_CONFIG_FILENAME = os.path.join(
|
BASE_CONFIG_FILENAME = os.path.join(
|
||||||
os.path.dirname(__file__),
|
os.path.dirname(__file__),
|
||||||
'../config/base.ini',
|
"../config/base.ini"
|
||||||
)
|
)
|
||||||
ENV_CONFIG_FILENAME = os.path.join(
|
ENV_CONFIG_FILENAME = os.path.join(
|
||||||
os.path.dirname(__file__),
|
os.path.dirname(__file__),
|
||||||
'../config/',
|
"../config/",
|
||||||
'{}.ini'.format(os.getenv('TORNADO_ENV', 'dev').lower())
|
"{}.ini".format(ENV.lower())
|
||||||
)
|
)
|
||||||
config = ConfigParser()
|
config = ConfigParser()
|
||||||
|
|
||||||
|
@ -25,3 +25,13 @@ class BaseHandler(tornado.web.RequestHandler):
|
|||||||
ns = super(BaseHandler, self).get_template_namespace()
|
ns = super(BaseHandler, self).get_template_namespace()
|
||||||
ns.update(helpers)
|
ns.update(helpers)
|
||||||
return ns
|
return ns
|
||||||
|
|
||||||
|
def get_current_user(self):
|
||||||
|
if self.get_secure_cookie('atst'):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
False
|
||||||
|
|
||||||
|
# this is a temporary implementation until we have real sessions
|
||||||
|
def _start_session(self):
|
||||||
|
self.set_secure_cookie('atst', 'valid-user-session')
|
||||||
|
13
atst/handlers/dev.py
Normal file
13
atst/handlers/dev.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from atst.handler import BaseHandler
|
||||||
|
|
||||||
|
class Dev(BaseHandler):
|
||||||
|
def initialize(self, action):
|
||||||
|
self.action = action
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
if self.action == 'login':
|
||||||
|
self._login()
|
||||||
|
|
||||||
|
def _login(self):
|
||||||
|
self._start_session()
|
||||||
|
self.redirect("/home")
|
10
atst/handlers/home.py
Normal file
10
atst/handlers/home.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import tornado
|
||||||
|
from atst.handler import BaseHandler
|
||||||
|
|
||||||
|
class Home(BaseHandler):
|
||||||
|
|
||||||
|
def initialize(self, page):
|
||||||
|
self.page = page
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render( '%s.html.to' % self.page, page = self.page )
|
37
atst/handlers/login.py
Normal file
37
atst/handlers/login.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import tornado
|
||||||
|
from atst.handler import BaseHandler
|
||||||
|
|
||||||
|
|
||||||
|
class Login(BaseHandler):
|
||||||
|
|
||||||
|
def initialize(self, authnid_client):
|
||||||
|
self.authnid_client = authnid_client
|
||||||
|
|
||||||
|
@tornado.gen.coroutine
|
||||||
|
def get(self):
|
||||||
|
token = self.get_query_argument("bearer-token")
|
||||||
|
if token:
|
||||||
|
valid = yield self._validate_login_token(token)
|
||||||
|
if valid:
|
||||||
|
self._start_session()
|
||||||
|
self.redirect("/home")
|
||||||
|
return
|
||||||
|
|
||||||
|
url = self.get_login_url()
|
||||||
|
self.redirect(url)
|
||||||
|
return
|
||||||
|
|
||||||
|
@tornado.gen.coroutine
|
||||||
|
def _validate_login_token(self, token):
|
||||||
|
try:
|
||||||
|
response = yield self.authnid_client.post(
|
||||||
|
"/api/v1/validate", json={"token": token}
|
||||||
|
)
|
||||||
|
return response.code == 200
|
||||||
|
|
||||||
|
except tornado.httpclient.HTTPError as error:
|
||||||
|
if error.response.code == 401:
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise error
|
@ -1,3 +1,5 @@
|
|||||||
|
import atst
|
||||||
|
import tornado
|
||||||
from atst.handler import BaseHandler
|
from atst.handler import BaseHandler
|
||||||
|
|
||||||
class MainHandler(BaseHandler):
|
class MainHandler(BaseHandler):
|
||||||
@ -5,5 +7,6 @@ class MainHandler(BaseHandler):
|
|||||||
def initialize(self, page):
|
def initialize(self, page):
|
||||||
self.page = page
|
self.page = page
|
||||||
|
|
||||||
|
@tornado.web.authenticated
|
||||||
def get(self):
|
def get(self):
|
||||||
self.render( '%s.html.to' % self.page, page = self.page )
|
self.render( '%s.html.to' % self.page, page = self.page )
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import tornado
|
||||||
from atst.handler import BaseHandler
|
from atst.handler import BaseHandler
|
||||||
|
|
||||||
mock_requests = [
|
mock_requests = [
|
||||||
@ -31,5 +32,6 @@ class Request(BaseHandler):
|
|||||||
def initialize(self, page):
|
def initialize(self, page):
|
||||||
self.page = page
|
self.page = page
|
||||||
|
|
||||||
|
@tornado.web.authenticated
|
||||||
def get(self):
|
def get(self):
|
||||||
self.render('requests.html.to', page = self.page, requests = mock_requests )
|
self.render('requests.html.to', page = self.page, requests = mock_requests )
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import tornado
|
||||||
from atst.handler import BaseHandler
|
from atst.handler import BaseHandler
|
||||||
|
|
||||||
class RequestNew(BaseHandler):
|
class RequestNew(BaseHandler):
|
||||||
@ -22,6 +23,7 @@ class RequestNew(BaseHandler):
|
|||||||
def initialize(self, page):
|
def initialize(self, page):
|
||||||
self.page = page
|
self.page = page
|
||||||
|
|
||||||
|
@tornado.web.authenticated
|
||||||
def get(self, screen = 1):
|
def get(self, screen = 1):
|
||||||
self.render( 'requests/screen-%d.html.to' % int(screen),
|
self.render( 'requests/screen-%d.html.to' % int(screen),
|
||||||
page = self.page,
|
page = self.page,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from atst.handler import BaseHandler
|
from atst.handler import BaseHandler
|
||||||
import requests
|
import tornado
|
||||||
import tornado.gen
|
import tornado.gen
|
||||||
|
|
||||||
mock_workspaces = [
|
mock_workspaces = [
|
||||||
@ -13,8 +13,6 @@ mock_workspaces = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
session = requests.Session()
|
|
||||||
|
|
||||||
class Workspace(BaseHandler):
|
class Workspace(BaseHandler):
|
||||||
|
|
||||||
def initialize(self, page, authz_client):
|
def initialize(self, page, authz_client):
|
||||||
@ -22,5 +20,6 @@ class Workspace(BaseHandler):
|
|||||||
self.authz_client = authz_client
|
self.authz_client = authz_client
|
||||||
|
|
||||||
@tornado.gen.coroutine
|
@tornado.gen.coroutine
|
||||||
|
@tornado.web.authenticated
|
||||||
def get(self):
|
def get(self):
|
||||||
self.render( 'workspaces.html.to', page = self.page, workspaces = mock_workspaces )
|
self.render( 'workspaces.html.to', page = self.page, workspaces = mock_workspaces )
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
[default]
|
[default]
|
||||||
|
PORT=8000
|
||||||
ENVIRONMENT = dev
|
ENVIRONMENT = dev
|
||||||
DEBUG = true
|
DEBUG = true
|
||||||
AUTHZ_BASE_URL = http://localhost
|
AUTHZ_BASE_URL = http://localhost
|
||||||
PORT = 8000
|
AUTHNID_BASE_URL= http://localhost
|
||||||
|
COOKIE_SECRET = some-secret-please-replace
|
||||||
|
55
tests/test_auth.py
Normal file
55
tests/test_auth.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import re
|
||||||
|
import pytest
|
||||||
|
import tornado.web
|
||||||
|
import tornado.gen
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gen_test
|
||||||
|
def test_redirects_when_not_logged_in(http_client, base_url):
|
||||||
|
response = yield http_client.fetch(
|
||||||
|
base_url + "/home", raise_error=False, follow_redirects=False
|
||||||
|
)
|
||||||
|
location = response.headers['Location']
|
||||||
|
assert response.code == 302
|
||||||
|
assert response.error
|
||||||
|
assert re.match('/\??', location)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gen_test
|
||||||
|
def test_login_with_valid_bearer_token(app, monkeypatch, http_client, base_url):
|
||||||
|
@tornado.gen.coroutine
|
||||||
|
def _validate_login_token(c, t):
|
||||||
|
return True
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"atst.handlers.login.Login._validate_login_token",
|
||||||
|
_validate_login_token
|
||||||
|
)
|
||||||
|
response = yield http_client.fetch(
|
||||||
|
base_url + "/login?bearer-token=abc-123",
|
||||||
|
follow_redirects=False,
|
||||||
|
raise_error=False
|
||||||
|
)
|
||||||
|
assert response.headers["Set-Cookie"].startswith("atst")
|
||||||
|
assert response.headers['Location'] == '/home'
|
||||||
|
assert response.code == 302
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gen_test
|
||||||
|
def test_login_via_dev_endpoint(app, http_client, base_url):
|
||||||
|
response = yield http_client.fetch(
|
||||||
|
base_url + "/login-dev", raise_error=False, follow_redirects=False
|
||||||
|
)
|
||||||
|
assert response.headers["Set-Cookie"].startswith("atst")
|
||||||
|
assert response.code == 302
|
||||||
|
assert response.headers["Location"] == "/home"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gen_test
|
||||||
|
@pytest.mark.skip(reason="need to work out auth error user paths")
|
||||||
|
def test_login_with_invalid_bearer_token(http_client, base_url):
|
||||||
|
response = yield http_client.fetch(
|
||||||
|
base_url + "/home",
|
||||||
|
raise_error=False,
|
||||||
|
headers={"Cookie": "bearer-token=anything"},
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user