Skip to content

Commit 3ad801d

Browse files
authored
Merge pull request #5 from CESNET/devel
Beta v0.2
2 parents b0d24ef + f5ba7b3 commit 3ad801d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+1267
-275
lines changed

api/README.md

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
## How to use API as WSGI (example configuration for Apache)
22

3+
This example is for Apache VirtualHost instance on https. You can of course use it without the virtualhost but remeber to include the whole Liberouter GUI part and if required, change paths accordingly.
4+
35
```
46
<VirtualHost *:443>
5-
DocumentRoot "/var/www/dev"
7+
DocumentRoot "/var/www/html"
68
ServerName example.com
79
SSLEngine on
810
SSLCertificateFile "/etc/apache2/server.crt"
@@ -11,17 +13,18 @@
1113
ErrorLog "/var/log/apache2/secure-error_log"
1214
CustomLog "/var/log/apache2/secure-access_log" common
1315
14-
#Options Includes FollowSymLinks MultiViews
15-
16+
# Liberouter GUI WSGI
1617
WSGIDaemonProcess libapi user=liberouter group=liberouter threads=5
17-
WSGIScriptAlias "/libapi" "/var/www/dev/liberouter-gui/wsgi.py"
18+
WSGIScriptAlias "/libapi" "/var/www/html/liberouter-gui/api/wsgi.py"
19+
WSGIPassAuthorization on
1820
19-
<directory "/var/www/dev/liberouter-gui">
21+
<directory "/var/www/html/liberouter-gui/api">
2022
WSGIProcessGroup libapi
2123
WSGIApplicationGroup %{GLOBAL}
2224
WSGIScriptReloading On
2325
Order deny,allow
2426
Allow from all
2527
</directory>
28+
# END Liberouter GUI WSGI
2629
</VirtualHost>
2730
```

start_api.py renamed to api/__main__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python3
22

3-
from api import app, config
3+
from liberouterapi import app, config
44

55
if __name__ == '__main__':
66
app.run(port = app.config["PORT"],

api/auth.py

-105
This file was deleted.

api/config-sample.ini

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[api]
2+
debug = false
3+
secret_key = super-secret
4+
;host =
5+
port = 5555
6+
threaded = true
7+
version = 1.0
8+
modules = /modules
9+
;cors = false
10+
; timeout in seconds
11+
;session_timeout = 10
12+
13+
[database]
14+
; possible values: sqlite, mysql, mongodb
15+
; sqlite: file must be specified, the server and port are ignored
16+
; mysql: server, port and database must be specified, user and password
17+
; are for authentication to the db
18+
; mongodb: server, port and database must be set
19+
provider = mongodb
20+
server = localhost
21+
port = 27017
22+
;user =
23+
;password =
24+
database = liberouter
25+
users = users
26+
27+
[ssl]
28+
enabled = false
29+
;key =
30+
;certificate =

api/dbConnector.py

-54
This file was deleted.

api/error.py

-18
This file was deleted.

api/liberouterapi/Auth.py

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import bcrypt
2+
from functools import wraps
3+
from flask import request
4+
from datetime import datetime, timedelta
5+
6+
from .user import User
7+
from .role import Role
8+
from .session import SessionException
9+
from .error import ApiException
10+
11+
class AuthException(ApiException):
12+
status_code = 404
13+
14+
class Auth(object):
15+
errors = {
16+
'0' : 'Username not found.',
17+
'1' : 'Username and password doesn\'t match.',
18+
'2' : 'Expired session.',
19+
'3' : 'Authorization header is missing.'
20+
}
21+
22+
def __init__(self, db, session_manager, secret_key):
23+
self.db = db
24+
self.session_manager = session_manager
25+
self.secret_key = secret_key
26+
27+
@classmethod
28+
def check_password(self, password, hash):
29+
return bcrypt.checkpw(password.encode('utf8'), hash)
30+
31+
@classmethod
32+
def create_hash(self, password):
33+
return bcrypt.hashpw(password.encode('utf8'), bcrypt.gensalt())
34+
35+
def auth_error(self, code):
36+
msg = {
37+
'code' : code,
38+
'description' : self.errors[str(code)]
39+
}
40+
res = json_util.dumps(msg)
41+
return msg
42+
43+
def login(self, user):
44+
query = {
45+
'username' : user.username
46+
}
47+
48+
res = self.db.users.find_one(query)
49+
50+
if not res:
51+
raise AuthException("User not found")
52+
53+
if not self.check_password(user.password, res['password']):
54+
raise AuthException("Password mismatch")
55+
56+
# Remove password field from the user
57+
del res['password']
58+
59+
return(res)
60+
61+
def store_session(self, user):
62+
session_id = self.session_manager.create(user)
63+
return session_id
64+
65+
def lookup(self, session_id):
66+
try:
67+
session = self.session_manager.lookup(session_id)
68+
return session
69+
except SessionException:
70+
print("Couldn't find given session")
71+
raise
72+
73+
def delete(self, session_id):
74+
try:
75+
self.session_manager.delete(session_id)
76+
except SessionException:
77+
print("Couldn't find given session")
78+
raise
79+
80+
def required(self, role=Role.undefined):
81+
"""
82+
Decorator for required Authorization JWT token
83+
84+
Usage: (auth is the initialized Auth object instance)
85+
@auth.required() - Don't look for user's role.
86+
Only check if they have valid session.
87+
88+
@auth.required(Role.[admin|user|guest]) - check session validity and their role
89+
"""
90+
def auth_decorator(f):
91+
@wraps(f)
92+
def verify(*args, **kwargs):
93+
session_id = request.headers.get('Authorization', None)
94+
if not session_id:
95+
raise SessionException("Header field 'Authorization' not found.")
96+
97+
try:
98+
session = self.lookup(session_id)
99+
except SessionException:
100+
raise SessionException("Session not found")
101+
102+
if role != Role.undefined and role < session["user"].role:
103+
raise SessionException("Insufficient privileges.")
104+
105+
return f(*args, **kwargs)
106+
return verify
107+
return auth_decorator

api/liberouterapi/Response.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from flask import Response
2+
from bson import json_util
3+
4+
class ResponseHandler(Response):
5+
def __init__(self, content = None, *args, **kwargs):
6+
if isinstance(content, (dict, list)):
7+
content = json_util.dumps(content)
8+
super(Response, self).__init__(content, *args, **kwargs)
9+
10+
@classmethod
11+
def force_type(cls, rv, environ=None):
12+
if isinstance(rv, (dict, list)):
13+
return cls(rv)
14+
15+
return super(ResponseHandler, cls).force_type(rv, environ)

api/Router.py renamed to api/liberouterapi/Router.py

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from flask import Flask
2+
from .Response import ResponseHandler
23

34
class Router(Flask):
5+
response_class = ResponseHandler
6+
47
def add_route(self, rule, view_func, **options):
58
"""
69
Encapsulate add_url_rule(self, rule, endpoint=None, view_func=None, **options)

0 commit comments

Comments
 (0)