summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Dolgov <ivan@dolgov.fi>2018-06-29 16:27:18 +0300
committerLars Wirzenius <liw@liw.fi>2018-07-12 11:13:13 +0300
commit33b21c676584f7a1cf584b0aeb0127e45ca0d443 (patch)
tree70c6fdf5ea17a42dabd37238715bf1c19c8c425b
parent5e68c02a915ff50a9b8bc14b7f68c255be92cf21 (diff)
downloadqvisqve-33b21c676584f7a1cf584b0aeb0127e45ca0d443.tar.gz
Add: trivial user login flow
-rw-r--r--qvisqve/__init__.py3
-rw-r--r--qvisqve/api.py2
-rw-r--r--qvisqve/auth_router.py51
-rw-r--r--qvisqve/login_router.py39
-rw-r--r--qvisqve/responses.py9
-rw-r--r--qvisqve/token_router.py27
-rw-r--r--views/login.tpl18
7 files changed, 144 insertions, 5 deletions
diff --git a/qvisqve/__init__.py b/qvisqve/__init__.py
index a1d80f9..32032b6 100644
--- a/qvisqve/__init__.py
+++ b/qvisqve/__init__.py
@@ -19,12 +19,15 @@ from .responses import (
created_response,
ok_response,
unauthorized_response,
+ found_response,
)
from .log_setup import setup_logging, log
from .token import TokenGenerator
from .router import Router
from .version_router import VersionRouter
+from .login_router import LoginRouter
+from .auth_router import AuthRouter
from .token_router import TokenRouter
from .api import API
diff --git a/qvisqve/api.py b/qvisqve/api.py
index 2dee954..a6d71ef 100644
--- a/qvisqve/api.py
+++ b/qvisqve/api.py
@@ -27,6 +27,8 @@ class API:
routers = [
qvisqve.VersionRouter(),
+ qvisqve.LoginRouter(),
+ qvisqve.AuthRouter(self._config.get('callback_url', 'localhost')),
qvisqve.TokenRouter(
self._create_token_generator(), self._get_clients()),
]
diff --git a/qvisqve/auth_router.py b/qvisqve/auth_router.py
new file mode 100644
index 0000000..d4f3ca6
--- /dev/null
+++ b/qvisqve/auth_router.py
@@ -0,0 +1,51 @@
+# Copyright (C) 2018 Ivan Dolgov
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import urllib.parse
+
+
+import qvisqve
+
+
+class AuthRouter(qvisqve.Router):
+
+ def __init__(self, callback_url):
+ super().__init__()
+ self._callback_url = callback_url
+
+ def get_routes(self):
+ return [
+ {
+ 'method': 'POST',
+ 'path': '/auth',
+ 'callback': self._auth,
+ 'needs-authorization': False,
+ },
+ ]
+
+ def _auth(self, content_type, body, *args, **kwargs):
+ if content_type != 'application/x-www-form-urlencoded':
+ return qvisqve.bad_request_response('Wrong content type')
+
+ # TODO:
+ # - perform actual auth
+ # - create and store auth code
+ # - use callback url provided in request
+
+ params = urllib.parse.urlencode({'code': 123})
+ url = '{}?{}'.format(self._callback_url, params)
+
+ return qvisqve.found_response('Redirect to callback url', url)
diff --git a/qvisqve/login_router.py b/qvisqve/login_router.py
new file mode 100644
index 0000000..275f175
--- /dev/null
+++ b/qvisqve/login_router.py
@@ -0,0 +1,39 @@
+# Copyright (C) 2018 Ivan Dolgov
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import bottle
+
+
+import qvisqve
+
+
+class LoginRouter(qvisqve.Router):
+
+ def get_routes(self):
+ return [
+ {
+ 'method': 'GET',
+ 'path': '/login',
+ 'callback': self._login,
+ 'needs-authorization': False,
+ },
+ ]
+
+ def _login(self, *args, **kwargs):
+ headers = {
+ 'Content-Type': 'text/html; charset=utf-8',
+ }
+ return qvisqve.ok_response(bottle.template('login'), headers=headers)
diff --git a/qvisqve/responses.py b/qvisqve/responses.py
index 281ec43..d36094e 100644
--- a/qvisqve/responses.py
+++ b/qvisqve/responses.py
@@ -57,3 +57,12 @@ def unauthorized_response(body):
'Content-Type': 'text/plain',
}
return response(apifw.HTTP_UNAUTHORIZED, body, headers)
+
+
+def found_response(body, location):
+ headers = {
+ 'Content-Type': 'text/plain',
+ 'Location': location,
+ }
+ # TODO: use apifw.FOUND_CREATED when available
+ return response(302, body, headers)
diff --git a/qvisqve/token_router.py b/qvisqve/token_router.py
index 9acc924..f87037e 100644
--- a/qvisqve/token_router.py
+++ b/qvisqve/token_router.py
@@ -47,14 +47,25 @@ class TokenRouter(qvisqve.Router):
if content_type != 'application/x-www-form-urlencoded':
return qvisqve.bad_request_response('Wrong content type')
- client_id, client_secret = bottle.request.auth
- if not self._clients.is_correct_secret(client_id, client_secret):
- return qvisqve.unauthorized_response('Unauthorized')
-
params = self._get_form_params(body)
grant_type = self._get_grant_type(params)
- if grant_type != 'client_credentials':
+ if grant_type == 'client_credentials':
+ client_id, client_secret = bottle.request.auth
+ if not self._clients.is_correct_secret(client_id, client_secret):
+ return qvisqve.unauthorized_response('Unauthorized')
+ elif grant_type == 'authorization_code':
+ code = self._get_code(params)
+ # FIXME
+ if code is None or code != '123':
+ return qvisqve.unauthorized_response('Unauthorized')
+ empty_token = self._generator.new_token('', '')
+ return qvisqve.ok_response({
+ 'access_token': empty_token,
+ 'token_type': 'bearer',
+ 'scope': '',
+ })
+ else:
return qvisqve.bad_request_response('Wrong grant type')
scope = self._get_scope(params)
@@ -85,6 +96,12 @@ class TokenRouter(qvisqve.Router):
return grant_type[0]
return None
+ def _get_code(self, params):
+ code = params.get('code')
+ if len(code) == 1:
+ return code[0]
+ return None
+
def _get_scope(self, params):
scope = params.get('scope', [])
if len(scope) > 1:
diff --git a/views/login.tpl b/views/login.tpl
new file mode 100644
index 0000000..dae2c4c
--- /dev/null
+++ b/views/login.tpl
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="description" content="Qvisqve Login">
+ <meta name="author" content="Qvarnlabs Ltd">
+ <title>Qvisqve Login</title>
+ </head>
+ <body>
+ <form action="/auth" method="POST">
+ User name: <input name="username" type="text" />
+ <br />
+ Password: <input name="password" type="password" />
+ <br />
+ <input type="submit" value="Login" />
+ </form>
+ </body>
+</html>