login token and privilege check logic
Showing
14 changed files
with
213 additions
and
121 deletions
server/api/privilege.py
0 → 100644
1 | # -*- coding: utf-8 -*- | ||
2 | |||
3 | |||
4 | from flask import request | ||
5 | |||
6 | from server.database import db_adapter | ||
7 | from server.database.models import User, UserToken | ||
8 | from server.utils import get_now | ||
9 | from server.utils.api_response import unauthorized, forbidden | ||
10 | |||
11 | |||
12 | def token_required(func): | ||
13 | """ | ||
14 | User must login when this decorator is enabled | ||
15 | (for both user and admin) | ||
16 | """ | ||
17 | def authenticate_and_call(*args, **kwargs): | ||
18 | if not __validate_token(): | ||
19 | return unauthorized("login required") | ||
20 | return func(*args, **kwargs) | ||
21 | |||
22 | # keep the original func name for API intput/output validation | ||
23 | # where original name is required | ||
24 | authenticate_and_call.original = func.__name__ | ||
25 | if hasattr(func, "original"): | ||
26 | authenticate_and_call.original = func.original | ||
27 | |||
28 | return authenticate_and_call | ||
29 | |||
30 | |||
31 | def admin_privilege_required(func): | ||
32 | """ | ||
33 | user must login , hackathon_name must be available, | ||
34 | and 'user' has proper admin privilege on this hackathon | ||
35 | """ | ||
36 | |||
37 | def authenticate_and_call(*args, **kwargs): | ||
38 | user = __validate_token() | ||
39 | if not user: | ||
40 | return unauthorized("login required") | ||
41 | |||
42 | if not user.role == 'admin': | ||
43 | return forbidden("Permission denied need admin role") | ||
44 | |||
45 | return func(*args, **kwargs) | ||
46 | |||
47 | # keep the original func name for API intput/output | ||
48 | # validation where original name is required | ||
49 | authenticate_and_call.original = func.__name__ | ||
50 | if hasattr(func, "original"): | ||
51 | authenticate_and_call.original = func.original | ||
52 | |||
53 | return authenticate_and_call | ||
54 | |||
55 | |||
56 | def __validate_token(): | ||
57 | if 'token' not in request.headers: | ||
58 | return False | ||
59 | |||
60 | t = db_adapter.find_first_object_by(UserToken, | ||
61 | token=request.headers['token']) | ||
62 | if not t or t.expire_date <= get_now(): | ||
63 | return False | ||
64 | |||
65 | return t.user |
... | @@ -19,4 +19,4 @@ Base = declarative_base() | ... | @@ -19,4 +19,4 @@ Base = declarative_base() |
19 | Base.query = db_session.query_property() | 19 | Base.query = db_session.query_property() |
20 | db_adapter = SQLAlchemyAdapter(db_session) | 20 | db_adapter = SQLAlchemyAdapter(db_session) |
21 | 21 | ||
22 | from models import * | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
22 | from models import * | ... | ... |
... | @@ -6,6 +6,8 @@ from . import Base, db_adapter | ... | @@ -6,6 +6,8 @@ from . import Base, db_adapter |
6 | from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Text, TypeDecorator | 6 | from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Text, TypeDecorator |
7 | from sqlalchemy.orm import relation, backref | 7 | from sqlalchemy.orm import relation, backref |
8 | 8 | ||
9 | from server.utils import get_now | ||
10 | |||
9 | 11 | ||
10 | def relationship(*arg, **kw): | 12 | def relationship(*arg, **kw): |
11 | ret = relation(*arg, **kw) | 13 | ret = relation(*arg, **kw) |
... | @@ -56,78 +58,32 @@ class DBBase(Base): | ... | @@ -56,78 +58,32 @@ class DBBase(Base): |
56 | return '%s: %s' % (self.__class__.__name__, self.json()) | 58 | return '%s: %s' % (self.__class__.__name__, self.json()) |
57 | 59 | ||
58 | 60 | ||
59 | class Host(DBBase): | 61 | class User(DBBase): |
60 | __tablename__ = 'host' | 62 | __tablename__ = 'user' |
61 | |||
62 | id = Column(Integer, primary_key=True) | ||
63 | hostname = Column(String(50), unique=True, nullable=False) | ||
64 | public_ip = Column(String(50), unique=True, nullable=False) | ||
65 | private_ip = Column(String(50), unique=True, nullable=False) | ||
66 | mem = Column(String(50), nullable=False) | ||
67 | cores = Column(Integer, nullable=False) | ||
68 | disk = Column(String(50), nullable=False) | ||
69 | create_time = Column(DateTime, nullable=False) | ||
70 | update_time = Column(DateTime) | ||
71 | |||
72 | |||
73 | class VM(DBBase): | ||
74 | __tablename__ = 'vm' | ||
75 | 63 | ||
76 | id = Column(Integer, primary_key=True) | 64 | id = Column(Integer, primary_key=True) |
77 | vm_name = Column(String(50), unique=True, nullable=False) | 65 | name = Column(String(64)) |
78 | os_type = Column(String(50), nullable=False) # constants 0:linux 1:windows | 66 | password = Column(String(128)) |
79 | cores = Column(Integer, nullable=False) | 67 | role = Column(String(16)) |
80 | mem = Column(String(50), nullable=False) | 68 | status = Column(String(32)) |
81 | capacity_g = Column(Integer, nullable=False) | 69 | create_time = Column(DateTime, default=get_now()) |
82 | config = Column(String(50)) | 70 | last_login_time = Column(DateTime, default=get_now()) |
83 | user = Column(String(50), nullable=False) | ||
84 | create_time = Column(DateTime, nullable=False) | ||
85 | update_time = Column(DateTime) | ||
86 | |||
87 | image_id = Column(Integer, ForeignKey('image.id', ondelete='CASCADE')) | ||
88 | image = relationship('Image', backref=backref('images_vms', lazy='dynamic')) | ||
89 | |||
90 | host_id = Column(Integer, ForeignKey('host.id', ondelete='CASCADE')) | ||
91 | host = relationship('Host', backref=backref('host_vms', lazy='dynamic')) | ||
92 | |||
93 | 71 | ||
94 | class Network(DBBase): | 72 | def __init__(self, **kwargs): |
95 | __tablename__ = "network" | 73 | super(User, self).__init__(**kwargs) |
96 | |||
97 | id = Column(Integer, primary_key=True) | ||
98 | mac = Column(String(50), unique=True, nullable=False) | ||
99 | type = Column(String(50), nullable=False) # constants 0:public 1:private | ||
100 | address = Column(String(50), unique=True, nullable=False) | ||
101 | device = Column(String(50), nullable=False) | ||
102 | create_time = Column(DateTime, nullable=False) | ||
103 | update_time = Column(DateTime) | ||
104 | |||
105 | vm_id = Column(Integer, ForeignKey('vm.id', ondelete='CASCADE')) | ||
106 | vm = relationship('VM', backref=backref('networks', lazy='dynamic')) | ||
107 | 74 | ||
108 | 75 | ||
109 | class Disk(DBBase): | 76 | class UserToken(DBBase): |
110 | __tablename__ = "disk" | 77 | __tablename__ = 'user_token' |
111 | 78 | ||
112 | id = Column(Integer, primary_key=True) | 79 | id = Column(Integer, primary_key=True) |
113 | type = Column(Integer, nullable=True) # constants 0:system 1:mounted | 80 | token = Column(String(64), unique=True, nullable=False) |
114 | capacity_g = Column(Integer, nullable=False) | ||
115 | format = Column(Integer, nullable=False) # constants 0:ntfs 1:ext4 | ||
116 | path = Column(String(50), unique=True, nullable=False) | ||
117 | create_time = Column(DateTime, nullable=False) | ||
118 | update_time = Column(DateTime) | ||
119 | 81 | ||
120 | vm_id = Column(Integer, ForeignKey('vm.id', ondelete='CASCADE')) | 82 | user_id = Column(Integer, ForeignKey('user.id', ondelete='CASCADE')) |
121 | vm = relationship('VM', backref=backref('disks', lazy='dynamic')) | 83 | user = relationship('User', backref=backref('tokens', lazy='dynamic')) |
122 | 84 | ||
85 | create_time = Column(DateTime, default=get_now()) | ||
86 | expire_time = Column(DateTime, nullable=False) | ||
123 | 87 | ||
124 | class Image(DBBase): | 88 | def __init__(self, **kwargs): |
125 | __tablename__ = "image" | 89 | super(UserToken, self).__init__(**kwargs) |
126 | |||
127 | id = Column(Integer, primary_key=True) | ||
128 | name = Column(String(50), unique=True, nullable=False) | ||
129 | type = Column(Integer, nullable=True) # constants 0:default 1:provide 2:customize | ||
130 | path = Column(String(50), unique=True, nullable=False) | ||
131 | create_by = Column(String(50)) | ||
132 | create_time = Column(DateTime, nullable=False) | ||
133 | update_time = Column(DateTime) | ... | ... |
1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
2 | 2 | ||
3 | __author__ = 'hubian' | ||
4 | 3 | ||
5 | import logging | 4 | import logging |
6 | from os.path import realpath, dirname | 5 | from os.path import realpath, dirname |
7 | from logging import config, DEBUG, INFO | 6 | from logging import config, DEBUG, INFO |
8 | 7 | ||
8 | |||
9 | class Log(object): | 9 | class Log(object): |
10 | """Wrapper of Python logging module for easier usage | 10 | """Wrapper of Python logging module for easier usage |
11 | 11 | ||
... | @@ -61,6 +61,9 @@ class Log(object): | ... | @@ -61,6 +61,9 @@ class Log(object): |
61 | self.logger.critical(critical) | 61 | self.logger.critical(critical) |
62 | 62 | ||
63 | def __init__(self): | 63 | def __init__(self): |
64 | """initialize the log wrapper through 'logging.conf' file which should be in the same dir of this file""" | 64 | """ |
65 | initialize the log wrapper through 'logging.conf' file | ||
66 | which should be in the same dir of this file | ||
67 | """ | ||
65 | logging.config.fileConfig("%s/logging.conf" % dirname(realpath(__file__))) | 68 | logging.config.fileConfig("%s/logging.conf" % dirname(realpath(__file__))) |
66 | self.logger = logging.getLogger("myLogger") | 69 | self.logger = logging.getLogger("myLogger") |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
server/services/host/__init__.py
deleted
100644 → 0
1 | # -*- coding: utf-8 -*- |
server/services/user.py
0 → 100644
1 | # -*- coding: utf-8 -*- | ||
2 | |||
3 | |||
4 | import uuid | ||
5 | |||
6 | from datetime import timedelta | ||
7 | from flask import request, g | ||
8 | |||
9 | from server.database import db_adapter | ||
10 | from server.database import models | ||
11 | from server.log import log | ||
12 | from server.utils import get_now | ||
13 | from server.utils.api_response import ok, bad_request, internal_server_error | ||
14 | |||
15 | |||
16 | class UserService(object): | ||
17 | |||
18 | def logout(self, user_id): | ||
19 | pass | ||
20 | |||
21 | def login(self, username, password): | ||
22 | user = db_adapter.find_first_object_by(models.User, | ||
23 | name=username, | ||
24 | password=password) | ||
25 | if not user: | ||
26 | return bad_request("login failed") | ||
27 | else: | ||
28 | user_token = self.__generate_api_token(user) | ||
29 | return { | ||
30 | "user": user.to_dict(), | ||
31 | "token": user_token.token() | ||
32 | } | ||
33 | |||
34 | # --------------- helper private functions ---------------------# | ||
35 | |||
36 | def __generate_api_token(self, user): | ||
37 | token_issue_date = get_now() | ||
38 | token_expire_date = token_issue_date + timedelta(minutes=30) | ||
39 | user_token = models.UserToken(token=str(uuid.uuid1()), | ||
40 | user=user, | ||
41 | expire_date=token_expire_date, | ||
42 | issue_date=token_issue_date) | ||
43 | user_token.save() | ||
44 | return user_token |
server/services/user/user_service.py
deleted
100644 → 0
File mode changed
server/services/vm/__init__.py
deleted
100644 → 0
server/services/vm/vm_manager.py
deleted
100644 → 0
1 | # -*- coding: utf-8 -*- | ||
2 | |||
3 | __author__ = 'hubian' | ||
4 | |||
5 | |||
6 | class VMManager(): | ||
7 | |||
8 | def create_vm(self): | ||
9 | pass | ||
10 | |||
11 | def update_vm(self): | ||
12 | pass | ||
13 | |||
14 | def delete_vm(self): | ||
15 | pass | ||
16 | |||
17 | def get_vm_list(self): | ||
18 | pass | ||
19 | |||
20 | # ------------------------helper functions ---------------------- # | ||
21 | |||
22 | def __generate_vm_xml(self): | ||
23 | pass |
1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
2 | 2 | ||
3 | 3 | ||
4 | from factory import factory | 4 | from datetime import datetime |
5 | 5 | ||
6 | from server.database import db_session | ||
7 | from server.database.db_adapters import SQLAlchemyAdapter | ||
8 | from server.log.log import Log | ||
9 | from utils import Utils | ||
10 | 6 | ||
7 | def get_now(): | ||
8 | return datetime.now() | ||
11 | 9 | ||
12 | def init_factory(): | ||
13 | factory.provide("util", Utils) | ||
14 | factory.provide("log", Log) | ||
15 | factory.provide("db", SQLAlchemyAdapter, db_session) | ||
16 | 10 | ||
11 | def get_config(): | ||
12 | return None | ||
13 | |||
14 | |||
15 | def get_safe_config(): | ||
16 | return None | ... | ... |
server/utils/api_response.py
0 → 100644
1 | # -*- coding: utf-8 -*- | ||
2 | |||
3 | |||
4 | from server.log import log | ||
5 | |||
6 | |||
7 | # Customize Formatted Response | ||
8 | # with status code 200 and the real status code and message in body | ||
9 | |||
10 | |||
11 | def ok(data=None, message=""): | ||
12 | return { | ||
13 | "status": "success", | ||
14 | "code": 200, | ||
15 | "data": data, | ||
16 | "message": message | ||
17 | } | ||
18 | |||
19 | |||
20 | def __response_with_code(code, message, friendly_message=""): | ||
21 | log.debug("response with code: %d and message: %s" % (code, message)) | ||
22 | return { | ||
23 | "status": "failed", | ||
24 | "code": code, | ||
25 | "message": message, | ||
26 | "friendly_message": friendly_message | ||
27 | } | ||
28 | |||
29 | |||
30 | def bad_request(message="", | ||
31 | friendly_message=( | ||
32 | 'Your request does not make sense' | ||
33 | )): | ||
34 | return __response_with_code(400, message, friendly_message) | ||
35 | |||
36 | |||
37 | def unauthorized(message="", | ||
38 | friendly_message=( | ||
39 | 'Please login to get a valid token')): | ||
40 | return __response_with_code(401, message, friendly_message) | ||
41 | |||
42 | |||
43 | def forbidden(message="", | ||
44 | friendly_message=( | ||
45 | 'You don\'t have the permission to access the request')): | ||
46 | return __response_with_code(403, message, friendly_message) | ||
47 | |||
48 | |||
49 | def not_found(message="", | ||
50 | friendly_message=( | ||
51 | 'The requested URL was not found on the server.')): | ||
52 | return __response_with_code(404, message, friendly_message) | ||
53 | |||
54 | |||
55 | def internal_server_error(message="", | ||
56 | friendly_message=( | ||
57 | 'Encountered an internal error ,' | ||
58 | 'unable to complete your request.' | ||
59 | 'Either the server is overloaded or there ' | ||
60 | 'is an error in the application.' | ||
61 | )): | ||
62 | return __response_with_code(500, message, friendly_message) | ||
63 | |||
64 | |||
65 |
-
Please register or sign in to post a comment