mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
JWT login and multi API keys!
This commit is contained in:
parent
ea3f36809d
commit
93857f90db
@ -18,6 +18,7 @@ from app.classes.shared.server import Server
|
||||
from app.classes.minecraft.server_props import ServerProps
|
||||
from app.classes.minecraft.serverjars import server_jar_obj
|
||||
from app.classes.minecraft.stats import Stats
|
||||
from app.classes.models.users import ApiKeys
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -70,3 +71,7 @@ class Crafty_Perms_Controller:
|
||||
@staticmethod
|
||||
def add_server_creation(user_id):
|
||||
return crafty_permissions.add_server_creation(user_id)
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_permissions_list(key: ApiKeys):
|
||||
return crafty_permissions.get_api_key_permissions_list(key)
|
||||
|
@ -31,10 +31,6 @@ class Management_Controller:
|
||||
def get_latest_hosts_stats():
|
||||
return management_helper.get_latest_hosts_stats()
|
||||
|
||||
@staticmethod
|
||||
def new_api_token():
|
||||
return management_helper.new_api_token()
|
||||
|
||||
#************************************************************************************************
|
||||
# Commands Methods
|
||||
#************************************************************************************************
|
||||
|
@ -39,7 +39,9 @@ class Roles_Controller:
|
||||
|
||||
|
||||
@staticmethod
|
||||
def update_role(role_id, role_data={}, permissions_mask="00000000"):
|
||||
def update_role(role_id: str, role_data = None, permissions_mask: str = "00000000"):
|
||||
if role_data is None:
|
||||
role_data = {}
|
||||
base_data = Roles_Controller.get_role_with_servers(role_id)
|
||||
up_data = {}
|
||||
added_servers = set()
|
||||
|
@ -14,7 +14,7 @@ from app.classes.shared.console import console
|
||||
|
||||
from app.classes.shared.main_models import db_helper
|
||||
from app.classes.models.server_permissions import server_permissions, Enum_Permissions_Server
|
||||
from app.classes.models.users import users_helper
|
||||
from app.classes.models.users import users_helper, ApiKeys
|
||||
from app.classes.models.roles import roles_helper
|
||||
from app.classes.models.servers import servers_helper
|
||||
|
||||
@ -42,11 +42,6 @@ class Server_Perms_Controller:
|
||||
permissions_list = server_permissions.get_role_permissions_list(role_id)
|
||||
return permissions_list
|
||||
|
||||
@staticmethod
|
||||
def get_server_permissions_foruser(user_id, server_id):
|
||||
permissions_list = server_permissions.get_user_permissions_list(user_id, server_id)
|
||||
return permissions_list
|
||||
|
||||
@staticmethod
|
||||
def add_role_server(server_id, role_id, rs_permissions="00000000"):
|
||||
return server_permissions.add_role_server(server_id, role_id, rs_permissions)
|
||||
@ -78,8 +73,30 @@ class Server_Perms_Controller:
|
||||
return server_permissions.get_role_permissions_list(role_id)
|
||||
|
||||
@staticmethod
|
||||
def get_user_permissions_list(user_id, server_id):
|
||||
return server_permissions.get_user_permissions_list(user_id, server_id)
|
||||
def get_user_id_permissions_list(user_id: str, server_id: str):
|
||||
return server_permissions.get_user_id_permissions_list(user_id, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_id_permissions_list(key_id: str, server_id: str):
|
||||
key = users_helper.get_user_api_key(key_id)
|
||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
|
||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_user_id_permissions_list(user_id: str, server_id: str):
|
||||
return server_permissions.get_user_id_permissions_list(user_id, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_id_permissions_list(key_id: str, server_id: str):
|
||||
key = users_helper.get_user_api_key(key_id)
|
||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
|
||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_authorized_servers_stats_from_roles(user_id):
|
||||
|
@ -17,7 +17,7 @@ from app.classes.shared.console import console
|
||||
from app.classes.shared.main_models import db_helper
|
||||
from app.classes.models.servers import servers_helper
|
||||
from app.classes.models.roles import roles_helper
|
||||
from app.classes.models.users import users_helper
|
||||
from app.classes.models.users import users_helper, ApiKeys
|
||||
from app.classes.models.server_permissions import server_permissions, Enum_Permissions_Server
|
||||
|
||||
from app.classes.shared.server import Server
|
||||
@ -82,18 +82,42 @@ class Servers_Controller:
|
||||
return servers_helper.get_all_servers_stats()
|
||||
|
||||
@staticmethod
|
||||
def get_authorized_servers_stats(user_id):
|
||||
def get_authorized_servers_stats_api_key(api_key: ApiKeys):
|
||||
server_data = []
|
||||
authorized_servers = Servers_Controller.get_authorized_servers(user_id)
|
||||
authorized_servers = Servers_Controller.get_authorized_servers(api_key.user.user_id)
|
||||
|
||||
for s in authorized_servers:
|
||||
latest = servers_helper.get_latest_server_stats(s.get('server_id'))
|
||||
user_permissions = server_permissions.get_user_permissions_list(user_id, s.get('server_id'))
|
||||
key_permissions = server_permissions.get_api_key_permissions_list(api_key, s.get('server_id'))
|
||||
if Enum_Permissions_Server.Commands in key_permissions:
|
||||
user_command_permission = True
|
||||
else:
|
||||
user_command_permission = False
|
||||
server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0],
|
||||
"user_command_permission": user_command_permission})
|
||||
return server_data
|
||||
|
||||
@staticmethod
|
||||
def get_authorized_servers_stats(user_id):
|
||||
server_data = []
|
||||
print('test 1')
|
||||
authorized_servers = Servers_Controller.get_authorized_servers(user_id)
|
||||
print('test 2')
|
||||
|
||||
for s in authorized_servers:
|
||||
latest = servers_helper.get_latest_server_stats(s.get('server_id'))
|
||||
# TODO
|
||||
user_permissions = server_permissions.get_user_id_permissions_list(user_id, s.get('server_id'))
|
||||
if Enum_Permissions_Server.Commands in user_permissions:
|
||||
user_command_permission = True
|
||||
else:
|
||||
user_command_permission = False
|
||||
server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0], "user_command_permission":user_command_permission})
|
||||
server_data.append({
|
||||
'server_data': s,
|
||||
'stats': db_helper.return_rows(latest)[0],
|
||||
'user_command_permission': user_command_permission
|
||||
})
|
||||
|
||||
return server_data
|
||||
|
||||
@staticmethod
|
||||
@ -112,17 +136,21 @@ class Servers_Controller:
|
||||
return servers_helper.server_id_exists(server_id)
|
||||
|
||||
@staticmethod
|
||||
def server_id_authorized(serverId, user_id):
|
||||
authorized = 0
|
||||
def server_id_authorized(server_id_a, user_id):
|
||||
print("Server id authorized: ")
|
||||
user_roles = users_helper.user_role_query(user_id)
|
||||
for role in user_roles:
|
||||
authorized = server_permissions.get_role_servers_from_role_id(role.role_id)
|
||||
|
||||
#authorized = db_helper.return_rows(authorized)
|
||||
|
||||
if authorized.count() == 0:
|
||||
return False
|
||||
for server_id_b in server_permissions.get_role_servers_from_role_id(role.role_id):
|
||||
if server_id_a == server_id_b:
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def server_id_authorized_api_key(server_id: str, api_key: ApiKeys) -> bool:
|
||||
# TODO
|
||||
return Servers_Controller.server_id_authorized(server_id, api_key.user.user_id)
|
||||
# There is no view server permission
|
||||
# permission_helper.both_have_perm(api_key)
|
||||
|
||||
@staticmethod
|
||||
def set_update(server_id, value):
|
||||
|
@ -2,6 +2,8 @@ import os
|
||||
import time
|
||||
import logging
|
||||
import sys
|
||||
from typing import Optional
|
||||
|
||||
import yaml
|
||||
import asyncio
|
||||
import shutil
|
||||
@ -13,6 +15,7 @@ from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
|
||||
from app.classes.models.users import Users, users_helper
|
||||
from app.classes.shared.authentication import authentication
|
||||
from app.classes.models.crafty_permissions import crafty_permissions, Enum_Permissions_Crafty
|
||||
from app.classes.models.management import management_helper
|
||||
|
||||
@ -31,10 +34,6 @@ class Users_Controller:
|
||||
def get_id_by_name(username):
|
||||
return users_helper.get_user_id_by_name(username)
|
||||
|
||||
@staticmethod
|
||||
def get_user_by_api_token(token: str):
|
||||
return users_helper.get_user_by_api_token(token)
|
||||
|
||||
@staticmethod
|
||||
def get_user_lang_by_id(user_id):
|
||||
return users_helper.get_user_lang_by_id(user_id)
|
||||
@ -52,7 +51,11 @@ class Users_Controller:
|
||||
users_helper.set_support_path(user_id, support_path)
|
||||
|
||||
@staticmethod
|
||||
def update_user(user_id, user_data={}, user_crafty_data={}):
|
||||
def update_user(user_id: str, user_data=None, user_crafty_data=None):
|
||||
if user_crafty_data is None:
|
||||
user_crafty_data = {}
|
||||
if user_data is None:
|
||||
user_data = {}
|
||||
base_data = users_helper.get_user(user_id)
|
||||
up_data = {}
|
||||
added_roles = set()
|
||||
@ -64,9 +67,6 @@ class Users_Controller:
|
||||
elif key == "roles":
|
||||
added_roles = user_data['roles'].difference(base_data['roles'])
|
||||
removed_roles = base_data['roles'].difference(user_data['roles'])
|
||||
elif key == "regen_api":
|
||||
if user_data['regen_api']:
|
||||
up_data['api_token'] = management_helper.new_api_token()
|
||||
elif key == "password":
|
||||
if user_data['password'] is not None and user_data['password'] != "":
|
||||
up_data['password'] = helper.encode_pass(user_data['password'])
|
||||
@ -77,13 +77,12 @@ class Users_Controller:
|
||||
logger.debug("user: {} +role:{} -role:{}".format(user_data, added_roles, removed_roles))
|
||||
for role in added_roles:
|
||||
users_helper.get_or_create(user_id=user_id, role_id=role)
|
||||
# TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point
|
||||
permissions_mask = user_crafty_data.get('permissions_mask', '000')
|
||||
|
||||
if 'server_quantity' in user_crafty_data:
|
||||
limit_server_creation = user_crafty_data['server_quantity'][
|
||||
Enum_Permissions_Crafty.Server_Creation.name]
|
||||
|
||||
for key in user_crafty_data:
|
||||
if key == "permissions_mask":
|
||||
permissions_mask = user_crafty_data['permissions_mask']
|
||||
if key == "server_quantity":
|
||||
limit_server_creation = user_crafty_data['server_quantity'][Enum_Permissions_Crafty.Server_Creation.name]
|
||||
limit_user_creation = user_crafty_data['server_quantity'][Enum_Permissions_Crafty.User_Config.name]
|
||||
limit_role_creation = user_crafty_data['server_quantity'][Enum_Permissions_Crafty.Roles_Config.name]
|
||||
else:
|
||||
@ -98,8 +97,8 @@ class Users_Controller:
|
||||
users_helper.update_user(user_id, up_data)
|
||||
|
||||
@staticmethod
|
||||
def add_user(username, password=None, email="default@example.com", api_token=None, enabled=True, superuser=False):
|
||||
return users_helper.add_user(username, password=password, email=email, api_token=api_token, enabled=enabled, superuser=superuser)
|
||||
def add_user(username, password=None, email="default@example.com", enabled: bool = True, superuser: bool = False):
|
||||
return users_helper.add_user(username, password=password, email=email, enabled=enabled, superuser=superuser)
|
||||
|
||||
@staticmethod
|
||||
def remove_user(user_id):
|
||||
@ -109,9 +108,19 @@ class Users_Controller:
|
||||
def user_id_exists(user_id):
|
||||
return users_helper.user_id_exists(user_id)
|
||||
|
||||
#************************************************************************************************
|
||||
@staticmethod
|
||||
def get_user_id_by_api_token(token: str) -> str:
|
||||
token_data = authentication.check_no_iat(token)
|
||||
return token_data['user_id']
|
||||
|
||||
@staticmethod
|
||||
def get_user_by_api_token(token: str):
|
||||
_, user = authentication.check(token)
|
||||
return user
|
||||
|
||||
# ************************************************************************************************
|
||||
# User Roles Methods
|
||||
#************************************************************************************************
|
||||
# ************************************************************************************************
|
||||
|
||||
@staticmethod
|
||||
def get_user_roles_id(user_id):
|
||||
@ -132,3 +141,29 @@ class Users_Controller:
|
||||
@staticmethod
|
||||
def user_role_query(user_id):
|
||||
return users_helper.user_role_query(user_id)
|
||||
|
||||
# ************************************************************************************************
|
||||
# Api Keys Methods
|
||||
# ************************************************************************************************
|
||||
|
||||
@staticmethod
|
||||
def get_user_api_keys(user_id: str):
|
||||
return users_helper.get_user_api_keys(user_id)
|
||||
|
||||
@staticmethod
|
||||
def get_user_api_key(key_id: str):
|
||||
return users_helper.get_user_api_key(key_id)
|
||||
|
||||
@staticmethod
|
||||
def add_user_api_key(name: str, user_id: str, superuser: bool = False,
|
||||
server_permissions_mask: Optional[str] = None,
|
||||
crafty_permissions_mask: Optional[str] = None):
|
||||
return users_helper.add_user_api_key(name, user_id, superuser, server_permissions_mask, crafty_permissions_mask)
|
||||
|
||||
@staticmethod
|
||||
def delete_user_api_keys(user_id: str):
|
||||
return users_helper.delete_user_api_keys(user_id)
|
||||
|
||||
@staticmethod
|
||||
def delete_user_api_key(key_id: str):
|
||||
return users_helper.delete_user_api_key(key_id)
|
||||
|
@ -5,7 +5,8 @@ import datetime
|
||||
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
from app.classes.models.users import Users
|
||||
from app.classes.models.users import Users, ApiKeys
|
||||
from app.classes.shared.permission_helper import permission_helper
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
peewee_logger = logging.getLogger('peewee')
|
||||
@ -191,4 +192,18 @@ class Permissions_Crafty:
|
||||
User_Crafty.save(user_crafty)
|
||||
return user_crafty.created_server
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_permissions_list(key: ApiKeys):
|
||||
user = key.user
|
||||
if user.superuser and key.superuser:
|
||||
return crafty_permissions.get_permissions_list()
|
||||
else:
|
||||
user_permissions_mask = crafty_permissions.get_crafty_permissions_mask(user.user_id)
|
||||
key_permissions_mask: str = key.crafty_permissions
|
||||
permissions_mask = permission_helper.combine_masks(user_permissions_mask, key_permissions_mask)
|
||||
permissions_list = crafty_permissions.get_permissions(permissions_mask)
|
||||
return permissions_list
|
||||
|
||||
|
||||
|
||||
crafty_permissions = Permissions_Crafty()
|
@ -7,7 +7,8 @@ from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
from app.classes.models.servers import Servers
|
||||
from app.classes.models.roles import Roles
|
||||
from app.classes.models.users import users_helper
|
||||
from app.classes.models.users import users_helper, ApiKeys, Users
|
||||
from app.classes.shared.permission_helper import permission_helper
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
peewee_logger = logging.getLogger('peewee')
|
||||
@ -78,10 +79,7 @@ class Permissions_Servers:
|
||||
|
||||
@staticmethod
|
||||
def has_permission(permission_mask, permission_tested: Enum_Permissions_Server):
|
||||
result = False
|
||||
if permission_mask[permission_tested.value] == '1':
|
||||
result = True
|
||||
return result
|
||||
return permission_mask[permission_tested.value] == '1'
|
||||
|
||||
@staticmethod
|
||||
def set_permission(permission_mask, permission_tested: Enum_Permissions_Server, value):
|
||||
@ -94,6 +92,14 @@ class Permissions_Servers:
|
||||
def get_permission(permission_mask, permission_tested: Enum_Permissions_Server):
|
||||
return permission_mask[permission_tested.value]
|
||||
|
||||
@staticmethod
|
||||
def get_token_permissions(permissions_mask, api_permissions_mask):
|
||||
permissions_list = []
|
||||
for member in Enum_Permissions_Server.__members__.items():
|
||||
if permission_helper.both_have_perm(permissions_mask, api_permissions_mask, member[1]):
|
||||
permissions_list.append(member[1])
|
||||
return permissions_list
|
||||
|
||||
|
||||
#************************************************************************************************
|
||||
# Role_Servers Methods
|
||||
@ -146,7 +152,9 @@ class Permissions_Servers:
|
||||
Role_Servers.save(role_server)
|
||||
|
||||
@staticmethod
|
||||
def delete_roles_permissions(role_id, removed_servers={}):
|
||||
def delete_roles_permissions(role_id, removed_servers=None):
|
||||
if removed_servers is None:
|
||||
removed_servers = {}
|
||||
return Role_Servers.delete().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id.in_(removed_servers)).execute()
|
||||
|
||||
@staticmethod
|
||||
@ -155,21 +163,52 @@ class Permissions_Servers:
|
||||
return Role_Servers.delete().where(Role_Servers.server_id == server_id).execute()
|
||||
|
||||
@staticmethod
|
||||
def get_user_permissions_list(user_id, server_id):
|
||||
permissions_mask = ''
|
||||
permissions_list = []
|
||||
def get_user_id_permissions_mask(user_id, server_id: str):
|
||||
user = users_helper.get_user_model(user_id)
|
||||
return server_permissions.get_user_permissions_mask(user, server_id)
|
||||
|
||||
user = users_helper.get_user(user_id)
|
||||
if user['superuser'] == True:
|
||||
@staticmethod
|
||||
def get_user_permissions_mask(user: Users, server_id: str):
|
||||
if user.superuser:
|
||||
permissions_mask = '1' * len(server_permissions.get_permissions_list())
|
||||
else:
|
||||
roles_list = users_helper.get_user_roles_id(user['user_id'])
|
||||
role_server = Role_Servers.select().where(Role_Servers.role_id.in_(roles_list)).where(Role_Servers.server_id == server_id).execute()
|
||||
permissions_mask = role_server[0].permissions
|
||||
return permissions_mask
|
||||
|
||||
@staticmethod
|
||||
def get_user_id_permissions_list(user_id, server_id: str):
|
||||
user = users_helper.get_user_model(user_id)
|
||||
return server_permissions.get_user_permissions_list(user, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_user_permissions_list(user: Users, server_id: str):
|
||||
if user.superuser:
|
||||
permissions_list = server_permissions.get_permissions_list()
|
||||
else:
|
||||
roles_list = users_helper.get_user_roles_id(user_id)
|
||||
role_server = Role_Servers.select().where(Role_Servers.role_id.in_(roles_list)).where(Role_Servers.server_id == int(server_id)).execute()
|
||||
if len(role_server) > 0:
|
||||
permissions_mask = role_server[0].permissions
|
||||
else:
|
||||
permissions_mask = '00000000'
|
||||
permissions_mask = server_permissions.get_user_permissions_mask(user, server_id)
|
||||
permissions_list = server_permissions.get_permissions(permissions_mask)
|
||||
return permissions_list
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_id_permissions_list(key_id, server_id: str):
|
||||
key = ApiKeys.get(ApiKeys.token_id == key_id)
|
||||
return server_permissions.get_api_key_permissions_list(key, server_id)
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_permissions_list(key: ApiKeys, server_id: str):
|
||||
user = key.user
|
||||
if user.superuser and key.superuser:
|
||||
return server_permissions.get_permissions_list()
|
||||
else:
|
||||
roles_list = users_helper.get_user_roles_id(user['user_id'])
|
||||
role_server = Role_Servers.select().where(Role_Servers.role_id.in_(roles_list)).where(Role_Servers.server_id == server_id).execute()
|
||||
user_permissions_mask = role_server[0].permissions
|
||||
key_permissions_mask = key.server_permissions
|
||||
permissions_mask = permission_helper.combine_masks(user_permissions_mask, key_permissions_mask)
|
||||
permissions_list = server_permissions.get_permissions(permissions_mask)
|
||||
return permissions_list
|
||||
|
||||
|
||||
server_permissions = Permissions_Servers()
|
@ -2,6 +2,7 @@ import os
|
||||
import sys
|
||||
import logging
|
||||
import datetime
|
||||
from typing import Optional, List, Union
|
||||
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
@ -41,14 +42,32 @@ class Users(Model):
|
||||
email = CharField(default="default@example.com")
|
||||
enabled = BooleanField(default=True)
|
||||
superuser = BooleanField(default=False)
|
||||
api_token = CharField(default="", unique=True, index=True) # we may need to revisit this
|
||||
lang = CharField(default="en_EN")
|
||||
support_logs = CharField(default = '')
|
||||
valid_tokens_from = DateTimeField(default=datetime.datetime.now)
|
||||
|
||||
class Meta:
|
||||
table_name = "users"
|
||||
database = database
|
||||
|
||||
|
||||
# ************************************************************************************************
|
||||
# API Keys Class
|
||||
# ************************************************************************************************
|
||||
class ApiKeys(Model):
|
||||
token_id = AutoField()
|
||||
name = CharField(default='', unique=True, index=True)
|
||||
created = DateTimeField(default=datetime.datetime.now)
|
||||
user = ForeignKeyField(Users, backref='api_token', index=True)
|
||||
server_permissions = CharField(default='00000000')
|
||||
crafty_permissions = CharField(default='000')
|
||||
superuser = BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
table_name = 'api_keys'
|
||||
database = database
|
||||
|
||||
|
||||
#************************************************************************************************
|
||||
# User Roles Class
|
||||
#************************************************************************************************
|
||||
@ -86,18 +105,6 @@ class helper_users:
|
||||
except DoesNotExist:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_user_by_api_token(token: str):
|
||||
query = Users.select().where(Users.api_token == token)
|
||||
|
||||
if query.exists():
|
||||
user = model_to_dict(Users.get(Users.api_token == token))
|
||||
# I know it should apply it without setting it but I'm just making sure
|
||||
user = users_helper.add_user_roles(user)
|
||||
return user
|
||||
else:
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def user_query(user_id):
|
||||
user_query = Users.select().where(Users.user_id == user_id)
|
||||
@ -117,7 +124,6 @@ class helper_users:
|
||||
'email': "default@example.com",
|
||||
'enabled': True,
|
||||
'superuser': True,
|
||||
'api_token': None,
|
||||
'roles': [],
|
||||
'servers': [],
|
||||
'support_logs': '',
|
||||
@ -140,21 +146,21 @@ class helper_users:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def add_user(username, password=None, email=None, api_token=None, enabled=True, superuser=False):
|
||||
def get_user_model(user_id: str) -> Users:
|
||||
user = Users.get(Users.user_id == user_id)
|
||||
user = users_helper.add_user_roles(user)
|
||||
return user
|
||||
|
||||
@staticmethod
|
||||
def add_user(username: str, password: Optional[str] = None, email: Optional[str] = None, enabled: bool = True, superuser: bool = False) -> str:
|
||||
if password is not None:
|
||||
pw_enc = helper.encode_pass(password)
|
||||
else:
|
||||
pw_enc = None
|
||||
if api_token is None:
|
||||
api_token = users_helper.new_api_token()
|
||||
else:
|
||||
if type(api_token) is not str and len(api_token) != 32:
|
||||
raise ValueError("API token must be a 32 character string")
|
||||
user_id = Users.insert({
|
||||
Users.username: username.lower(),
|
||||
Users.password: pw_enc,
|
||||
Users.email: email,
|
||||
Users.api_token: api_token,
|
||||
Users.enabled: enabled,
|
||||
Users.superuser: superuser,
|
||||
Users.created: helper.get_time_as_string()
|
||||
@ -162,7 +168,9 @@ class helper_users:
|
||||
return user_id
|
||||
|
||||
@staticmethod
|
||||
def update_user(user_id, up_data={}):
|
||||
def update_user(user_id, up_data=None):
|
||||
if up_data is None:
|
||||
up_data = {}
|
||||
if up_data:
|
||||
Users.update(up_data).where(Users.user_id == user_id).execute()
|
||||
|
||||
@ -183,14 +191,6 @@ class helper_users:
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def new_api_token():
|
||||
while True:
|
||||
token = helper.random_string_generator(32)
|
||||
test = list(Users.select(Users.user_id).where(Users.api_token == token))
|
||||
if len(test) == 0:
|
||||
return token
|
||||
|
||||
#************************************************************************************************
|
||||
# User_Roles Methods
|
||||
#************************************************************************************************
|
||||
@ -223,7 +223,7 @@ class helper_users:
|
||||
}).execute()
|
||||
|
||||
@staticmethod
|
||||
def add_user_roles(user):
|
||||
def add_user_roles(user: Union[dict, Users]):
|
||||
if type(user) == dict:
|
||||
user_id = user['user_id']
|
||||
else:
|
||||
@ -237,7 +237,11 @@ class helper_users:
|
||||
for r in roles_query:
|
||||
roles.add(r.role_id.role_id)
|
||||
|
||||
if type(user) == dict:
|
||||
user['roles'] = roles
|
||||
else:
|
||||
user.roles = roles
|
||||
|
||||
#logger.debug("user: ({}) {}".format(user_id, user))
|
||||
return user
|
||||
|
||||
@ -257,5 +261,36 @@ class helper_users:
|
||||
def remove_roles_from_role_id(role_id):
|
||||
User_Roles.delete().where(User_Roles.role_id == role_id).execute()
|
||||
|
||||
# ************************************************************************************************
|
||||
# ApiKeys Methods
|
||||
# ************************************************************************************************
|
||||
|
||||
@staticmethod
|
||||
def get_user_api_keys(user_id: str):
|
||||
return ApiKeys.select().where(ApiKeys.user_id == user_id).execute()
|
||||
|
||||
@staticmethod
|
||||
def get_user_api_key(key_id: str) -> ApiKeys:
|
||||
return ApiKeys.get(ApiKeys.token_id == key_id)
|
||||
|
||||
@staticmethod
|
||||
def add_user_api_key(name: str, user_id: str, superuser: bool = False, server_permissions_mask: Optional[str] = None, crafty_permissions_mask: Optional[str] = None):
|
||||
return ApiKeys.insert({
|
||||
ApiKeys.name: name,
|
||||
ApiKeys.user_id: user_id,
|
||||
**({ApiKeys.server_permissions: server_permissions_mask} if server_permissions_mask is not None else {}),
|
||||
**({ApiKeys.crafty_permissions: crafty_permissions_mask} if crafty_permissions_mask is not None else {}),
|
||||
ApiKeys.superuser: superuser
|
||||
}).execute()
|
||||
|
||||
@staticmethod
|
||||
def delete_user_api_keys(user_id: str):
|
||||
ApiKeys.delete().where(ApiKeys.user_id == user_id).execute()
|
||||
|
||||
@staticmethod
|
||||
def delete_user_api_key(key_id: str):
|
||||
ApiKeys.delete().where(ApiKeys.token_id == key_id).execute()
|
||||
|
||||
|
||||
|
||||
users_helper = helper_users()
|
76
app/classes/shared/authentication.py
Normal file
76
app/classes/shared/authentication.py
Normal file
@ -0,0 +1,76 @@
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Dict, Any, Tuple
|
||||
|
||||
import jwt
|
||||
from jwt import PyJWTError
|
||||
|
||||
from app.classes.models.users import users_helper, ApiKeys
|
||||
from app.classes.shared.helpers import helper
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Authentication:
|
||||
def __init__(self):
|
||||
self.secret = "my secret"
|
||||
self.secret = helper.get_setting('apikey_secret', None)
|
||||
|
||||
if self.secret is None or self.secret == 'random':
|
||||
self.secret = helper.random_string_generator(64)
|
||||
|
||||
@staticmethod
|
||||
def generate(user_id, extra=None):
|
||||
if extra is None:
|
||||
extra = {}
|
||||
return jwt.encode(
|
||||
{
|
||||
'user_id': user_id,
|
||||
'iat': int(time.time()),
|
||||
**extra
|
||||
},
|
||||
authentication.secret,
|
||||
algorithm="HS256"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def read(token):
|
||||
return jwt.decode(token, authentication.secret, algorithms=["HS256"])
|
||||
|
||||
@staticmethod
|
||||
def check_no_iat(token) -> Optional[Dict[str, Any]]:
|
||||
try:
|
||||
return jwt.decode(token, authentication.secret, algorithms=["HS256"])
|
||||
except PyJWTError as error:
|
||||
logger.debug("Error while checking JWT token: ", exc_info=error)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def check(token) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]:
|
||||
try:
|
||||
data = jwt.decode(token, authentication.secret, algorithms=["HS256"])
|
||||
except PyJWTError as error:
|
||||
logger.debug("Error while checking JWT token: ", exc_info=error)
|
||||
return None
|
||||
iat: int = data['iat']
|
||||
key: Optional[ApiKeys] = None
|
||||
if 'token_id' in data:
|
||||
key_id = data['token_id']
|
||||
key = users_helper.get_user_api_key(key_id)
|
||||
if key is None:
|
||||
return None
|
||||
user_id: str = data['user_id']
|
||||
user = users_helper.get_user(user_id)
|
||||
# TODO: Have a cache or something so we don't constantly have to query the database
|
||||
if int(user.get('valid_tokens_from').timestamp()) < iat:
|
||||
# Success!
|
||||
return key, data, user
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def check_bool(token) -> bool:
|
||||
return authentication.check(token) is not None
|
||||
|
||||
|
||||
authentication = Authentication()
|
@ -41,29 +41,33 @@ class MainPrompt(cmd.Cmd, object):
|
||||
self.universal_exit()
|
||||
|
||||
def do_migrations(self, line):
|
||||
if (line == 'up'):
|
||||
if line == 'up':
|
||||
self.migration_manager.up()
|
||||
elif (line == 'down'):
|
||||
elif line == 'down':
|
||||
self.migration_manager.down()
|
||||
elif (line == 'done'):
|
||||
elif line == 'done':
|
||||
console.info(self.migration_manager.done)
|
||||
elif (line == 'todo'):
|
||||
elif line == 'todo':
|
||||
console.info(self.migration_manager.todo)
|
||||
elif (line == 'diff'):
|
||||
elif line == 'diff':
|
||||
console.info(self.migration_manager.diff)
|
||||
elif (line == 'info'):
|
||||
elif line == 'info':
|
||||
console.info('Done: {}'.format(self.migration_manager.done))
|
||||
console.info('FS: {}'.format(self.migration_manager.todo))
|
||||
console.info('Todo: {}'.format(self.migration_manager.diff))
|
||||
elif (line.startswith('add ')):
|
||||
elif line.startswith('add '):
|
||||
migration_name = line[len('add '):]
|
||||
self.migration_manager.create(migration_name, False)
|
||||
else:
|
||||
console.info('Unknown migration command')
|
||||
|
||||
def do_threads(self, line):
|
||||
@staticmethod
|
||||
def do_threads(_line):
|
||||
for thread in threading.enumerate():
|
||||
print(f'Name: {thread.name} IDENT: {thread.ident}')
|
||||
if sys.version_info >= (3, 8):
|
||||
print(f'Name: {thread.name} Identifier: {thread.ident} TID/PID: {thread.native_id}')
|
||||
else:
|
||||
print(f'Name: {thread.name} Identifier: {thread.ident}')
|
||||
|
||||
def universal_exit(self):
|
||||
logger.info("Stopping all server daemons / threads")
|
||||
@ -75,7 +79,6 @@ class MainPrompt(cmd.Cmd, object):
|
||||
sys.exit(0)
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def help_exit():
|
||||
console.help("Stops the server if running, Exits the program")
|
||||
|
@ -164,25 +164,6 @@ class Helpers:
|
||||
cmd_out[ci] += c
|
||||
return cmd_out
|
||||
|
||||
def check_for_old_logs(self, db_helper):
|
||||
servers = db_helper.get_all_defined_servers()
|
||||
for server in servers:
|
||||
logs_path = os.path.split(server['log_path'])[0]
|
||||
latest_log_file = os.path.split(server['log_path'])[1]
|
||||
logs_delete_after = int(server['logs_delete_after'])
|
||||
if logs_delete_after == 0:
|
||||
continue
|
||||
|
||||
log_files = list(filter(
|
||||
lambda val: val != latest_log_file,
|
||||
os.listdir(logs_path)
|
||||
))
|
||||
for log_file in log_files:
|
||||
log_file_path = os.path.join(logs_path, log_file)
|
||||
if self.check_file_exists(log_file_path) and \
|
||||
self.is_file_older_than_x_days(log_file_path, logs_delete_after):
|
||||
os.remove(log_file_path)
|
||||
|
||||
def get_setting(self, key, default_return=False):
|
||||
|
||||
try:
|
||||
|
@ -3,6 +3,8 @@ import pathlib
|
||||
import time
|
||||
import logging
|
||||
import sys
|
||||
from typing import Union
|
||||
|
||||
from app.classes.models.server_permissions import Enum_Permissions_Server
|
||||
from app.classes.models.users import helper_users
|
||||
from peewee import DoesNotExist
|
||||
@ -18,10 +20,16 @@ from app.classes.web.websocket_helper import websocket_helper
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
|
||||
#Importing Models
|
||||
from app.classes.models.crafty_permissions import crafty_permissions, Enum_Permissions_Crafty
|
||||
# Importing Models
|
||||
from app.classes.models.servers import servers_helper
|
||||
#Importing Controllers
|
||||
from app.classes.shared.console import console
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.server import Server
|
||||
from app.classes.minecraft.server_props import ServerProps
|
||||
from app.classes.minecraft.serverjars import server_jar_obj
|
||||
from app.classes.minecraft.stats import Stats
|
||||
|
||||
# Importing Controllers
|
||||
from app.classes.controllers.crafty_perms_controller import Crafty_Perms_Controller
|
||||
from app.classes.controllers.management_controller import Management_Controller
|
||||
from app.classes.controllers.users_controller import Users_Controller
|
||||
@ -29,11 +37,6 @@ from app.classes.controllers.roles_controller import Roles_Controller
|
||||
from app.classes.controllers.server_perms_controller import Server_Perms_Controller
|
||||
from app.classes.controllers.servers_controller import Servers_Controller
|
||||
|
||||
from app.classes.shared.server import Server
|
||||
from app.classes.minecraft.server_props import ServerProps
|
||||
from app.classes.minecraft.serverjars import server_jar_obj
|
||||
from app.classes.minecraft.stats import Stats
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Controller:
|
||||
@ -173,7 +176,7 @@ class Controller:
|
||||
|
||||
@staticmethod
|
||||
def add_system_user():
|
||||
helper_users.add_user("system", helper.random_string_generator(64), "default@example.com", helper_users.new_api_token(), False, False)
|
||||
helper_users.add_user("system", helper.random_string_generator(64), "default@example.com", False, False)
|
||||
|
||||
def get_server_settings(self, server_id):
|
||||
for s in self.servers_list:
|
||||
@ -183,17 +186,17 @@ class Controller:
|
||||
logger.warning("Unable to find server object for server id {}".format(server_id))
|
||||
return False
|
||||
|
||||
def get_server_obj(self, server_id):
|
||||
def get_server_obj(self, server_id: Union[str, int]) -> Union[bool, Server]:
|
||||
for s in self.servers_list:
|
||||
if int(s['server_id']) == int(server_id):
|
||||
if str(s['server_id']) == str(server_id):
|
||||
return s['server_obj']
|
||||
|
||||
logger.warning("Unable to find server object for server id {}".format(server_id))
|
||||
return False
|
||||
return False # TODO: Change to None
|
||||
|
||||
def get_server_data(self, server_id):
|
||||
def get_server_data(self, server_id: str):
|
||||
for s in self.servers_list:
|
||||
if int(s['server_id']) == int(server_id):
|
||||
if s['server_id'] == server_id:
|
||||
return s['server_data_obj']
|
||||
|
||||
logger.warning("Unable to find server object for server id {}".format(server_id))
|
||||
@ -406,7 +409,7 @@ class Controller:
|
||||
for s in self.servers_list:
|
||||
|
||||
# if this is the droid... im mean server we are looking for...
|
||||
if int(s['server_id']) == int(server_id):
|
||||
if s['server_id'] == server_id:
|
||||
server_data = self.get_server_data(server_id)
|
||||
server_name = server_data['server_name']
|
||||
backup_dir = self.servers.get_server_data_by_id(server_id)['backup_path']
|
||||
|
@ -10,6 +10,10 @@ from app.classes.minecraft.server_props import ServerProps
|
||||
from app.classes.web.websocket_helper import websocket_helper
|
||||
|
||||
|
||||
# To disable warning about unused import ; Users is imported from here in other places
|
||||
Users = Users
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
peewee_logger = logging.getLogger('peewee')
|
||||
peewee_logger.setLevel(logging.INFO)
|
||||
@ -39,20 +43,16 @@ class db_builder:
|
||||
|
||||
username = default_data.get("username", 'admin')
|
||||
password = default_data.get("password", 'crafty')
|
||||
#api_token = helper.random_string_generator(32)
|
||||
#
|
||||
#Users.insert({
|
||||
# Users.username: username.lower(),
|
||||
# Users.password: helper.encode_pass(password),
|
||||
# Users.api_token: api_token,
|
||||
# Users.enabled: True,
|
||||
# Users.superuser: True
|
||||
#}).execute()
|
||||
user_id = users_helper.add_user(username=username, password=password, email="default@example.com", superuser=True)
|
||||
#users_helper.update_user(user_id, user_crafty_data={"permissions_mask":"111", "server_quantity":[-1,-1,-1]} )
|
||||
|
||||
#console.info("API token is {}".format(api_token))
|
||||
|
||||
@staticmethod
|
||||
def is_fresh_install():
|
||||
try:
|
||||
|
@ -4,13 +4,9 @@ import typing as t
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from importlib import import_module
|
||||
from functools import wraps
|
||||
|
||||
try:
|
||||
from functools import cached_property
|
||||
except ImportError:
|
||||
from cached_property import cached_property
|
||||
from functools import cached_property
|
||||
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.shared.console import console
|
||||
@ -21,7 +17,7 @@ try:
|
||||
import peewee
|
||||
from playhouse.migrate import (
|
||||
SchemaMigrator as ScM,
|
||||
SqliteMigrator as SqM,
|
||||
SqliteMigrator,
|
||||
Operation, SQL, operation, SqliteDatabase,
|
||||
make_index_name, Context
|
||||
)
|
||||
@ -32,6 +28,22 @@ except ModuleNotFoundError as e:
|
||||
console.critical("Import Error: Unable to load {} module".format(e.name))
|
||||
sys.exit(1)
|
||||
|
||||
MIGRATE_TABLE = 'migratehistory'
|
||||
MIGRATE_TEMPLATE = '''# Generated by database migrator
|
||||
import peewee
|
||||
|
||||
def migrate(migrator, db):
|
||||
"""
|
||||
Write your migrations here.
|
||||
"""
|
||||
{migrate}
|
||||
|
||||
def rollback(migrator, db):
|
||||
"""
|
||||
Write your rollback migrations here.
|
||||
"""
|
||||
{rollback}'''
|
||||
|
||||
|
||||
class MigrateHistory(peewee.Model):
|
||||
"""
|
||||
@ -41,30 +53,15 @@ class MigrateHistory(peewee.Model):
|
||||
name = peewee.CharField(unique=True)
|
||||
migrated_at = peewee.DateTimeField(default=datetime.utcnow)
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
def __unicode__(self) -> str:
|
||||
"""
|
||||
String representation of this migration
|
||||
"""
|
||||
return self.name
|
||||
|
||||
|
||||
MIGRATE_TABLE = 'migratehistory'
|
||||
MIGRATE_TEMPLATE = '''# Generated by database migrator
|
||||
|
||||
|
||||
def migrate(migrator, database, **kwargs):
|
||||
"""
|
||||
Write your migrations here.
|
||||
"""
|
||||
{migrate}
|
||||
|
||||
|
||||
def rollback(migrator, database, **kwargs):
|
||||
"""
|
||||
Write your rollback migrations here.
|
||||
"""
|
||||
{rollback}'''
|
||||
VOID: t.Callable = lambda m, d: None
|
||||
class Meta:
|
||||
table_name = MIGRATE_TABLE
|
||||
|
||||
|
||||
def get_model(method):
|
||||
@ -75,11 +72,12 @@ def get_model(method):
|
||||
@wraps(method)
|
||||
def wrapper(migrator, model, *args, **kwargs):
|
||||
if isinstance(model, str):
|
||||
return method(migrator, migrator.orm[model], *args, **kwargs)
|
||||
return method(migrator, migrator.table_dict[model], *args, **kwargs)
|
||||
return method(migrator, model, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
class Migrator(object):
|
||||
def __init__(self, database: t.Union[peewee.Database, peewee.Proxy]):
|
||||
"""
|
||||
@ -88,8 +86,8 @@ class Migrator(object):
|
||||
if isinstance(database, peewee.Proxy):
|
||||
database = database.obj
|
||||
self.database: SqliteDatabase = database
|
||||
self.orm: t.Dict[str, peewee.Model] = {}
|
||||
self.operations: t.List[Operation] = []
|
||||
self.table_dict: t.Dict[str, peewee.Model] = {}
|
||||
self.operations: t.List[t.Union[Operation, callable]] = []
|
||||
self.migrator = SqliteMigrator(database)
|
||||
|
||||
def run(self):
|
||||
@ -113,13 +111,13 @@ class Migrator(object):
|
||||
"""
|
||||
Executes raw SQL.
|
||||
"""
|
||||
self.operations.append(self.migrator.sql(sql, *params))
|
||||
self.operations.append(SQL(sql, *params))
|
||||
|
||||
def create_table(self, model: peewee.Model) -> peewee.Model:
|
||||
"""
|
||||
Creates model and table in database.
|
||||
"""
|
||||
self.orm[model._meta.table_name] = model
|
||||
self.table_dict[model._meta.table_name] = model
|
||||
model._meta.database = self.database
|
||||
self.operations.append(model.create_table)
|
||||
return model
|
||||
@ -129,8 +127,8 @@ class Migrator(object):
|
||||
"""
|
||||
Drops model and table from database.
|
||||
"""
|
||||
del self.orm[model._meta.table_name]
|
||||
self.operations.append(self.migrator.drop_table(model))
|
||||
del self.table_dict[model._meta.table_name]
|
||||
self.operations.append(lambda: model.drop_table(cascade=False))
|
||||
|
||||
@get_model
|
||||
def add_columns(self, model: peewee.Model, **fields: peewee.Field) -> peewee.Model:
|
||||
@ -147,64 +145,16 @@ class Migrator(object):
|
||||
return model
|
||||
|
||||
@get_model
|
||||
def change_columns(self, model: peewee.Model, **fields: peewee.Field) -> peewee.Model:
|
||||
"""
|
||||
Changes fields.
|
||||
"""
|
||||
for name, field in fields.items():
|
||||
old_field = model._meta.fields.get(name, field)
|
||||
old_column_name = old_field and old_field.column_name
|
||||
|
||||
model._meta.add_field(name, field)
|
||||
|
||||
if isinstance(old_field, peewee.ForeignKeyField):
|
||||
self.operations.append(self.migrator.drop_foreign_key_constraint(
|
||||
model._meta.table_name, old_column_name))
|
||||
|
||||
if old_column_name != field.column_name:
|
||||
self.operations.append(
|
||||
self.migrator.rename_column(
|
||||
model._meta.table_name, old_column_name, field.column_name))
|
||||
|
||||
if isinstance(field, peewee.ForeignKeyField):
|
||||
on_delete = field.on_delete if field.on_delete else 'RESTRICT'
|
||||
on_update = field.on_update if field.on_update else 'RESTRICT'
|
||||
self.operations.append(self.migrator.add_foreign_key_constraint(
|
||||
model._meta.table_name, field.column_name,
|
||||
field.rel_model._meta.table_name, field.rel_field.name,
|
||||
on_delete, on_update))
|
||||
continue
|
||||
|
||||
self.operations.append(self.migrator.change_column(
|
||||
model._meta.table_name, field.column_name, field))
|
||||
|
||||
if field.unique == old_field.unique:
|
||||
continue
|
||||
|
||||
if field.unique:
|
||||
index = (field.column_name,), field.unique
|
||||
self.operations.append(self.migrator.add_index(
|
||||
model._meta.table_name, *index))
|
||||
model._meta.indexes.append(index)
|
||||
else:
|
||||
index = (field.column_name,), old_field.unique
|
||||
self.operations.append(self.migrator.drop_index(
|
||||
model._meta.table_name, *index))
|
||||
model._meta.indexes.remove(index)
|
||||
|
||||
return model
|
||||
|
||||
@get_model
|
||||
def drop_columns(self, model: peewee.Model, names: str, **kwargs) -> peewee.Model:
|
||||
def drop_columns(self, model: peewee.Model, names: str) -> peewee.Model:
|
||||
"""
|
||||
Removes fields from model.
|
||||
"""
|
||||
fields = [field for field in model._meta.fields.values()
|
||||
if field.name in names]
|
||||
cascade = kwargs.pop('cascade', True)
|
||||
for field in fields:
|
||||
self.__del_field__(model, field)
|
||||
if field.unique:
|
||||
# Drop unique index
|
||||
index_name = make_index_name(
|
||||
model._meta.table_name, [field.column_name])
|
||||
self.operations.append(self.migrator.drop_index(
|
||||
@ -250,16 +200,15 @@ class Migrator(object):
|
||||
Renames table in database.
|
||||
"""
|
||||
old_name = model._meta.table_name
|
||||
del self.orm[model._meta.table_name]
|
||||
del self.table_dict[model._meta.table_name]
|
||||
model._meta.table_name = new_name
|
||||
self.orm[model._meta.table_name] = model
|
||||
self.table_dict[model._meta.table_name] = model
|
||||
self.operations.append(self.migrator.rename_table(old_name, new_name))
|
||||
return model
|
||||
|
||||
@get_model
|
||||
def add_index(self, model: peewee.Model, *columns: str, **kwargs) -> peewee.Model:
|
||||
def add_index(self, model: peewee.Model, *columns: str, unique=False) -> peewee.Model:
|
||||
"""Create indexes."""
|
||||
unique = kwargs.pop('unique', False)
|
||||
model._meta.indexes.append((columns, unique))
|
||||
columns_ = []
|
||||
for col in columns:
|
||||
@ -329,42 +278,8 @@ class Migrator(object):
|
||||
return model
|
||||
|
||||
|
||||
class SqliteMigrator(SqM):
|
||||
def drop_table(self, model):
|
||||
return lambda: model.drop_table(cascade=False)
|
||||
|
||||
@operation
|
||||
def change_column(self, table: str, column_name: str, field: peewee.Field):
|
||||
operations = [self.alter_change_column(table, column_name, field)]
|
||||
if not field.null:
|
||||
operations.extend([self.add_not_null(table, column_name)])
|
||||
return operations
|
||||
|
||||
def alter_change_column(self, table: str, column_name: str, field: peewee.Field) -> Operation:
|
||||
return self._update_column(table, column_name, lambda x, y: y)
|
||||
|
||||
@operation
|
||||
def sql(self, sql: str, *params) -> SQL:
|
||||
"""
|
||||
Executes raw SQL.
|
||||
"""
|
||||
return SQL(sql, *params)
|
||||
|
||||
def alter_add_column(
|
||||
self, table: str, column_name: str, field: peewee.Field, **kwargs) -> Operation:
|
||||
"""
|
||||
Fixes field name for ForeignKeys.
|
||||
"""
|
||||
name = field.name
|
||||
op = super().alter_add_column(
|
||||
table, column_name, field, **kwargs)
|
||||
if isinstance(field, peewee.ForeignKeyField):
|
||||
field.name = name
|
||||
return op
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
class MigrationManager(object):
|
||||
|
||||
filemask = re.compile(r"[\d]+_[^\.]+\.py$")
|
||||
|
||||
def __init__(self, database: t.Union[peewee.Database, peewee.Proxy]):
|
||||
@ -376,7 +291,7 @@ class MigrationManager(object):
|
||||
self.database = database
|
||||
|
||||
@cached_property
|
||||
def model(self) -> peewee.Model:
|
||||
def model(self) -> t.Type[MigrateHistory]:
|
||||
"""
|
||||
Initialize and cache the MigrationHistory model.
|
||||
"""
|
||||
@ -487,7 +402,7 @@ class MigrationManager(object):
|
||||
scope = {}
|
||||
code = compile(code, '<string>', 'exec', dont_inherit=True)
|
||||
exec(code, scope, None)
|
||||
return scope.get('migrate', VOID), scope.get('rollback', VOID)
|
||||
return scope.get('migrate', lambda m, d: None), scope.get('rollback', lambda m, d: None)
|
||||
|
||||
def up_one(self, name: str, migrator: Migrator,
|
||||
fake: bool = False, rollback: bool = False) -> str:
|
||||
@ -518,11 +433,11 @@ class MigrationManager(object):
|
||||
|
||||
except Exception:
|
||||
self.database.rollback()
|
||||
operation = 'Rollback' if rollback else 'Migration'
|
||||
logger.exception('{} failed: {}'.format(operation, name))
|
||||
operation_name = 'Rollback' if rollback else 'Migration'
|
||||
logger.exception('{} failed: {}'.format(operation_name, name))
|
||||
raise
|
||||
|
||||
def down(self, name: t.Optional[str] = None):
|
||||
def down(self):
|
||||
"""
|
||||
Rolls back migrations.
|
||||
"""
|
||||
|
23
app/classes/shared/permission_helper.py
Normal file
23
app/classes/shared/permission_helper.py
Normal file
@ -0,0 +1,23 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class PermissionHelper:
|
||||
@staticmethod
|
||||
def both_have_perm(a: str, b: str, permission_tested: Enum):
|
||||
return permission_helper.combine_perm_bool(a[permission_tested.value], b[permission_tested.value])
|
||||
|
||||
@staticmethod
|
||||
def combine_perm(a: str, b: str) -> str:
|
||||
return '1' if (a == '1' and b == '1') else '0'
|
||||
|
||||
@staticmethod
|
||||
def combine_perm_bool(a: str, b: str) -> bool:
|
||||
return a == '1' and b == '1'
|
||||
|
||||
@staticmethod
|
||||
def combine_masks(permission_mask_a: str, permission_mask_b: str) -> str:
|
||||
both_masks = zip(list(permission_mask_a), list(permission_mask_b))
|
||||
return ''.join(map(lambda x: permission_helper.combine_perm(x[0], x[1]), both_masks))
|
||||
|
||||
|
||||
permission_helper = PermissionHelper()
|
@ -35,13 +35,13 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
@tornado.web.authenticated
|
||||
def get(self, page):
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
_, _, exec_user = self.current_user
|
||||
error = bleach.clean(self.get_argument('error', "WTF Error!"))
|
||||
|
||||
template = "panel/denied.html"
|
||||
|
||||
page_data = {
|
||||
'user_data': user_data,
|
||||
'user_data': exec_user,
|
||||
'error': error
|
||||
}
|
||||
|
||||
@ -164,10 +164,13 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
@tornado.web.authenticated
|
||||
def post(self, page):
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
api_key, _, exec_user = self.current_user
|
||||
superuser = exec_user['superuser']
|
||||
if api_key is not None:
|
||||
superuser = superuser and api_key.superuser
|
||||
|
||||
server_id = self.get_argument('id', None)
|
||||
exec_user_id = user_data['user_id']
|
||||
exec_user = helper_users.get_user(exec_user_id)
|
||||
|
||||
permissions = {
|
||||
'Commands': Enum_Permissions_Server.Commands,
|
||||
'Terminal': Enum_Permissions_Server.Terminal,
|
||||
@ -178,17 +181,17 @@ class AjaxHandler(BaseHandler):
|
||||
'Config': Enum_Permissions_Server.Config,
|
||||
'Players': Enum_Permissions_Server.Players,
|
||||
}
|
||||
user_perms = self.controller.server_perms.get_server_permissions_foruser(exec_user_id, server_id)
|
||||
user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id)
|
||||
error = bleach.clean(self.get_argument('error', "WTF Error!"))
|
||||
|
||||
page_data = {
|
||||
'user_data': user_data,
|
||||
'user_data': exec_user,
|
||||
'error': error
|
||||
}
|
||||
|
||||
if page == "send_command":
|
||||
command = self.get_body_argument('command', default=None, strip=True)
|
||||
server_id = self.get_argument('id')
|
||||
server_id = self.get_argument('id', None)
|
||||
|
||||
if server_id is None:
|
||||
logger.warning("Server ID not found in send_command ajax call")
|
||||
@ -200,11 +203,11 @@ class AjaxHandler(BaseHandler):
|
||||
if srv_obj.check_running():
|
||||
srv_obj.send_command(command)
|
||||
|
||||
self.controller.management.add_to_audit_log(user_data['user_id'], "Sent command to {} terminal: {}".format(self.controller.servers.get_server_friendly_name(server_id), command), server_id, self.get_remote_ip())
|
||||
self.controller.management.add_to_audit_log(exec_user['user_id'], "Sent command to {} terminal: {}".format(self.controller.servers.get_server_friendly_name(server_id), command), server_id, self.get_remote_ip())
|
||||
|
||||
elif page == "create_file":
|
||||
if not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
file_parent = helper.get_os_understandable_path(self.get_body_argument('file_parent', default=None, strip=True))
|
||||
@ -227,7 +230,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "create_dir":
|
||||
if not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
dir_parent = helper.get_os_understandable_path(self.get_body_argument('dir_parent', default=None, strip=True))
|
||||
@ -248,7 +251,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "unzip_file":
|
||||
if not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
server_id = self.get_argument('id', None)
|
||||
@ -259,7 +262,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "kill":
|
||||
if not permissions['Commands'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Commands")
|
||||
return
|
||||
server_id = self.get_argument('id', None)
|
||||
@ -272,11 +275,11 @@ class AjaxHandler(BaseHandler):
|
||||
elif page == "eula":
|
||||
server_id = self.get_argument('id', None)
|
||||
svr = self.controller.get_server_obj(server_id)
|
||||
svr.agree_eula(user_data['user_id'])
|
||||
svr.agree_eula(exec_user['user_id'])
|
||||
|
||||
elif page == "restore_backup":
|
||||
if not permissions['Backup'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Backups")
|
||||
return
|
||||
server_id = bleach.clean(self.get_argument('id', None))
|
||||
@ -295,16 +298,21 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "unzip_server":
|
||||
path = self.get_argument('path', None)
|
||||
helper.unzipServer(path, exec_user_id)
|
||||
helper.unzipServer(path, exec_user['user_id'])
|
||||
return
|
||||
|
||||
|
||||
@tornado.web.authenticated
|
||||
def delete(self, page):
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
api_key, _, exec_user = self.current_user
|
||||
superuser = exec_user['superuser']
|
||||
if api_key is not None:
|
||||
superuser = superuser and api_key.superuser
|
||||
|
||||
server_id = self.get_argument('id', None)
|
||||
exec_user_id = user_data['user_id']
|
||||
exec_user = helper_users.get_user(exec_user_id)
|
||||
|
||||
|
||||
|
||||
permissions = {
|
||||
'Commands': Enum_Permissions_Server.Commands,
|
||||
'Terminal': Enum_Permissions_Server.Terminal,
|
||||
@ -315,10 +323,10 @@ class AjaxHandler(BaseHandler):
|
||||
'Config': Enum_Permissions_Server.Config,
|
||||
'Players': Enum_Permissions_Server.Players,
|
||||
}
|
||||
user_perms = self.controller.server_perms.get_server_permissions_foruser(exec_user_id, server_id)
|
||||
user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id)
|
||||
if page == "del_file":
|
||||
if not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True))
|
||||
@ -350,7 +358,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
if page == "del_backup":
|
||||
if not permissions['Backup'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Backups")
|
||||
return
|
||||
file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True))
|
||||
@ -376,7 +384,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "del_dir":
|
||||
if not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
dir_path = helper.get_os_understandable_path(self.get_body_argument('dir_path', default=None, strip=True))
|
||||
@ -401,7 +409,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "delete_server":
|
||||
if not permissions['Config'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Config")
|
||||
return
|
||||
server_id = self.get_argument('id', None)
|
||||
@ -411,7 +419,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "delete_server_files":
|
||||
if not permissions['Config'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Config")
|
||||
return
|
||||
server_id = self.get_argument('id', None)
|
||||
@ -421,10 +429,12 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
@tornado.web.authenticated
|
||||
def put(self, page):
|
||||
user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
api_key, _, exec_user = self.current_user
|
||||
superuser = exec_user['superuser']
|
||||
if api_key is not None:
|
||||
superuser = superuser and api_key.superuser
|
||||
|
||||
server_id = self.get_argument('id', None)
|
||||
exec_user_id = user_data['user_id']
|
||||
exec_user = helper_users.get_user(exec_user_id)
|
||||
permissions = {
|
||||
'Commands': Enum_Permissions_Server.Commands,
|
||||
'Terminal': Enum_Permissions_Server.Terminal,
|
||||
@ -435,10 +445,10 @@ class AjaxHandler(BaseHandler):
|
||||
'Config': Enum_Permissions_Server.Config,
|
||||
'Players': Enum_Permissions_Server.Players,
|
||||
}
|
||||
user_perms = self.controller.server_perms.get_server_permissions_foruser(exec_user_id, server_id)
|
||||
user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id)
|
||||
if page == "save_file":
|
||||
if not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
file_contents = self.get_body_argument('file_contents', default=None, strip=True)
|
||||
@ -460,7 +470,7 @@ class AjaxHandler(BaseHandler):
|
||||
|
||||
elif page == "rename_item":
|
||||
if not permissions['Files'] in user_perms:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
self.redirect("/panel/error?error=Unauthorized access to Files")
|
||||
return
|
||||
item_path = helper.get_os_understandable_path(self.get_body_argument('item_path', default=None, strip=True))
|
||||
|
@ -1,14 +1,11 @@
|
||||
import os
|
||||
import secrets
|
||||
import threading
|
||||
import tornado.web
|
||||
import tornado.escape
|
||||
import logging
|
||||
import re
|
||||
|
||||
from app.classes.web.base_handler import BaseHandler
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
bearer_pattern = re.compile(r'^Bearer', flags=re.IGNORECASE)
|
||||
|
||||
class ApiHandler(BaseHandler):
|
||||
|
||||
@ -28,8 +25,14 @@ class ApiHandler(BaseHandler):
|
||||
def authenticate_user(self) -> bool:
|
||||
try:
|
||||
log.debug("Searching for specified token")
|
||||
# TODO: YEET THIS
|
||||
user_data = self.controller.users.get_user_by_api_token(self.get_argument('token'))
|
||||
|
||||
api_token = self.get_argument('token', '')
|
||||
if api_token is None and self.request.headers.get('Authorization'):
|
||||
api_token = bearer_pattern.sub('', self.request.headers.get('Authorization'))
|
||||
elif api_token is None:
|
||||
api_token = self.get_cookie('token')
|
||||
user_data = self.controller.users.get_user_by_api_token(api_token)
|
||||
|
||||
log.debug("Checking results")
|
||||
if user_data:
|
||||
# Login successful! Check perms
|
||||
@ -40,11 +43,11 @@ class ApiHandler(BaseHandler):
|
||||
else:
|
||||
logging.debug("Auth unsuccessful")
|
||||
self.access_denied("unknown", "the user provided an invalid token")
|
||||
return
|
||||
return False
|
||||
except Exception as e:
|
||||
log.warning("An error occured while authenticating an API user: %s", e)
|
||||
self.access_denied("unknown"), "an error occured while authenticating the user"
|
||||
return
|
||||
return False
|
||||
|
||||
|
||||
class ServersStats(ApiHandler):
|
||||
|
@ -4,10 +4,12 @@ import bleach
|
||||
from typing import (
|
||||
Union,
|
||||
List,
|
||||
Optional
|
||||
Optional, Tuple, Dict, Any
|
||||
)
|
||||
|
||||
from app.classes.shared.authentication import authentication
|
||||
from app.classes.shared.main_controller import Controller
|
||||
from app.classes.models.users import ApiKeys
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -17,7 +19,8 @@ class BaseHandler(tornado.web.RequestHandler):
|
||||
nobleach = {bool, type(None)}
|
||||
redactables = ("pass", "api")
|
||||
|
||||
def initialize(self, controller : Controller = None, tasks_manager=None, translator=None):
|
||||
# noinspection PyAttributeOutsideInit
|
||||
def initialize(self, controller: Controller = None, tasks_manager=None, translator=None):
|
||||
self.controller = controller
|
||||
self.tasks_manager = tasks_manager
|
||||
self.translator = translator
|
||||
@ -28,8 +31,9 @@ class BaseHandler(tornado.web.RequestHandler):
|
||||
self.request.remote_ip
|
||||
return remote_ip
|
||||
|
||||
def get_current_user(self):
|
||||
return self.get_secure_cookie("user", max_age_days=1)
|
||||
current_user: Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]
|
||||
def get_current_user(self) -> Optional[Tuple[Optional[ApiKeys], Dict[str, Any], Dict[str, Any]]]:
|
||||
return authentication.check(self.get_cookie("token"))
|
||||
|
||||
def autobleach(self, name, text):
|
||||
for r in self.redactables:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@ import requests
|
||||
import tornado.web
|
||||
import tornado.escape
|
||||
|
||||
from app.classes.shared.authentication import authentication
|
||||
from app.classes.shared.helpers import Helpers, helper
|
||||
from app.classes.web.base_handler import BaseHandler
|
||||
from app.classes.shared.console import console
|
||||
@ -27,7 +28,7 @@ except ModuleNotFoundError as e:
|
||||
|
||||
class PublicHandler(BaseHandler):
|
||||
|
||||
def set_current_user(self, user):
|
||||
def set_current_user(self, user_id: str = None):
|
||||
|
||||
expire_days = helper.get_setting('cookie_expire')
|
||||
|
||||
@ -35,8 +36,8 @@ class PublicHandler(BaseHandler):
|
||||
if not expire_days:
|
||||
expire_days = "5"
|
||||
|
||||
if user:
|
||||
self.set_secure_cookie("user", tornado.escape.json_encode(user), expires_days=int(expire_days))
|
||||
if user_id is not None:
|
||||
self.set_cookie("token", authentication.generate(user_id), expires_days=int(expire_days))
|
||||
else:
|
||||
self.clear_cookie("user")
|
||||
|
||||
@ -45,12 +46,7 @@ class PublicHandler(BaseHandler):
|
||||
error = bleach.clean(self.get_argument('error', "Invalid Login!"))
|
||||
error_msg = bleach.clean(self.get_argument('error_msg', ''))
|
||||
|
||||
page_data = {
|
||||
'version': helper.get_version_string(),
|
||||
'error': error
|
||||
}
|
||||
|
||||
page_data['lang'] = tornado.locale.get("en_EN")
|
||||
page_data = {'version': helper.get_version_string(), 'error': error, 'lang': tornado.locale.get("en_EN")}
|
||||
|
||||
# sensible defaults
|
||||
template = "public/404.html"
|
||||
@ -112,7 +108,7 @@ class PublicHandler(BaseHandler):
|
||||
|
||||
# Valid Login
|
||||
if login_result:
|
||||
self.set_current_user(entered_username)
|
||||
self.set_current_user(user_data.user_id)
|
||||
logger.info("User: {} Logged in from IP: {}".format(user_data, self.get_remote_ip()))
|
||||
|
||||
# record this login
|
||||
@ -140,15 +136,6 @@ class PublicHandler(BaseHandler):
|
||||
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
|
||||
else:
|
||||
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
|
||||
cookie_data = {
|
||||
"username": user_data.username,
|
||||
"user_id": user_data.user_id,
|
||||
"email": user_data.email,
|
||||
"profile_url": profile_url,
|
||||
"account_type": user_data.superuser,
|
||||
}
|
||||
|
||||
self.set_secure_cookie('user_data', json.dumps(cookie_data))
|
||||
|
||||
next_page = "/panel/dashboard"
|
||||
self.redirect(next_page)
|
||||
|
@ -28,13 +28,13 @@ class ServerHandler(BaseHandler):
|
||||
|
||||
@tornado.web.authenticated
|
||||
def get(self, page):
|
||||
# name = tornado.escape.json_decode(self.current_user)
|
||||
exec_user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
exec_user_id = exec_user_data['user_id']
|
||||
exec_user = self.controller.users.get_user_by_id(exec_user_id)
|
||||
api_key, token_data, exec_user = self.current_user
|
||||
superuser = exec_user['superuser']
|
||||
if api_key is not None:
|
||||
superuser = superuser and api_key.superuser
|
||||
|
||||
exec_user_role = set()
|
||||
if exec_user['superuser'] == 1:
|
||||
if superuser:
|
||||
defined_servers = self.controller.list_defined_servers()
|
||||
exec_user_role.add("Super User")
|
||||
exec_user_crafty_permissions = self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||
@ -42,8 +42,8 @@ class ServerHandler(BaseHandler):
|
||||
for role in self.controller.roles.get_all_roles():
|
||||
list_roles.append(self.controller.roles.get_role(role.role_id))
|
||||
else:
|
||||
exec_user_crafty_permissions = self.controller.crafty_perms.get_crafty_permissions_list(exec_user_id)
|
||||
defined_servers = self.controller.servers.get_authorized_servers(exec_user_id)
|
||||
exec_user_crafty_permissions = self.controller.crafty_perms.get_crafty_permissions_list(exec_user["user_id"])
|
||||
defined_servers = self.controller.servers.get_authorized_servers(exec_user["user_id"])
|
||||
list_roles = []
|
||||
for r in exec_user['roles']:
|
||||
role = self.controller.roles.get_role(r)
|
||||
@ -54,7 +54,7 @@ class ServerHandler(BaseHandler):
|
||||
|
||||
page_data = {
|
||||
'version_data': helper.get_version_string(),
|
||||
'user_data': exec_user_data,
|
||||
'user_data': exec_user,
|
||||
'user_role' : exec_user_role,
|
||||
'roles' : list_roles,
|
||||
'user_crafty_permissions' : exec_user_crafty_permissions,
|
||||
@ -71,13 +71,13 @@ class ServerHandler(BaseHandler):
|
||||
'hosts_data': self.controller.management.get_latest_hosts_stats(),
|
||||
'menu_servers': defined_servers,
|
||||
'show_contribute': helper.get_setting("show_contribute_link", True),
|
||||
'lang': self.controller.users.get_user_lang_by_id(exec_user_id)
|
||||
'lang': self.controller.users.get_user_lang_by_id(exec_user["user_id"])
|
||||
}
|
||||
if exec_user['superuser'] == 1:
|
||||
if superuser == 1:
|
||||
page_data['roles'] = list_roles
|
||||
|
||||
if page == "step1":
|
||||
if not exec_user['superuser'] and not self.controller.crafty_perms.can_create_server(exec_user_id):
|
||||
if not superuser and not self.controller.crafty_perms.can_create_server(exec_user["user_id"]):
|
||||
self.redirect("/panel/error?error=Unauthorized access: not a server creator or server limit reached")
|
||||
return
|
||||
|
||||
@ -93,17 +93,17 @@ class ServerHandler(BaseHandler):
|
||||
|
||||
@tornado.web.authenticated
|
||||
def post(self, page):
|
||||
|
||||
exec_user_data = json.loads(self.get_secure_cookie("user_data"))
|
||||
exec_user_id = exec_user_data['user_id']
|
||||
exec_user = self.controller.users.get_user_by_id(exec_user_id)
|
||||
api_key, token_data, exec_user = self.current_user
|
||||
superuser = exec_user['superuser']
|
||||
if api_key is not None:
|
||||
superuser = superuser and api_key.superuser
|
||||
|
||||
template = "public/404.html"
|
||||
page_data = {
|
||||
'version_data': "version_data_here",
|
||||
'user_data': exec_user_data,
|
||||
'version_data': "version_data_here", # TODO
|
||||
'user_data': exec_user,
|
||||
'show_contribute': helper.get_setting("show_contribute_link", True),
|
||||
'lang': self.controller.users.get_user_lang_by_id(exec_user_id)
|
||||
'lang': self.controller.users.get_user_lang_by_id(exec_user["user_id"])
|
||||
}
|
||||
|
||||
if page == "command":
|
||||
@ -151,11 +151,11 @@ class ServerHandler(BaseHandler):
|
||||
|
||||
return
|
||||
|
||||
self.controller.management.send_command(exec_user_data['user_id'], server_id, self.get_remote_ip(), command)
|
||||
self.controller.management.send_command(exec_user['user_id'], server_id, self.get_remote_ip(), command)
|
||||
|
||||
if page == "step1":
|
||||
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
user_roles = self.controller.roles.get_all_roles()
|
||||
else:
|
||||
user_roles = self.controller.roles.get_all_roles()
|
||||
@ -185,7 +185,7 @@ class ServerHandler(BaseHandler):
|
||||
return
|
||||
|
||||
new_server_id = self.controller.import_jar_server(server_name, import_server_path,import_server_jar, min_mem, max_mem, port)
|
||||
self.controller.management.add_to_audit_log(exec_user_data['user_id'],
|
||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
||||
"imported a jar server named \"{}\"".format(server_name), # Example: Admin imported a server named "old creative"
|
||||
new_server_id,
|
||||
self.get_remote_ip())
|
||||
@ -201,7 +201,7 @@ class ServerHandler(BaseHandler):
|
||||
if new_server_id == "false":
|
||||
self.redirect("/panel/error?error=Zip file not accessible! You can fix this permissions issue with sudo chown -R crafty:crafty {} And sudo chmod 2775 -R {}".format(import_server_path, import_server_path))
|
||||
return
|
||||
self.controller.management.add_to_audit_log(exec_user_data['user_id'],
|
||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
||||
"imported a zip server named \"{}\"".format(server_name), # Example: Admin imported a server named "old creative"
|
||||
new_server_id,
|
||||
self.get_remote_ip())
|
||||
@ -213,21 +213,21 @@ class ServerHandler(BaseHandler):
|
||||
return
|
||||
server_type, server_version = server_parts
|
||||
# TODO: add server type check here and call the correct server add functions if not a jar
|
||||
role_ids = self.controller.users.get_user_roles_id(exec_user_id)
|
||||
role_ids = self.controller.users.get_user_roles_id(exec_user["user_id"])
|
||||
new_server_id = self.controller.create_jar_server(server_type, server_version, server_name, min_mem, max_mem, port)
|
||||
self.controller.management.add_to_audit_log(exec_user_data['user_id'],
|
||||
self.controller.management.add_to_audit_log(exec_user['user_id'],
|
||||
"created a {} {} server named \"{}\"".format(server_version, str(server_type).capitalize(), server_name), # Example: Admin created a 1.16.5 Bukkit server named "survival"
|
||||
new_server_id,
|
||||
self.get_remote_ip())
|
||||
|
||||
# These lines create a new Role for the Server with full permissions and add the user to it if he's not a superuser
|
||||
if len(captured_roles) == 0:
|
||||
if not exec_user['superuser']:
|
||||
if not superuser:
|
||||
new_server_uuid = self.controller.servers.get_server_data_by_id(new_server_id).get("server_uuid")
|
||||
role_id = self.controller.roles.add_role("Creator of Server with uuid={}".format(new_server_uuid))
|
||||
self.controller.server_perms.add_role_server(new_server_id, role_id, "11111111")
|
||||
self.controller.users.add_role_to_user(exec_user_id, role_id)
|
||||
self.controller.crafty_perms.add_server_creation(exec_user_id)
|
||||
self.controller.users.add_role_to_user(exec_user["user_id"], role_id)
|
||||
self.controller.crafty_perms.add_server_creation(exec_user["user_id"])
|
||||
|
||||
else:
|
||||
for role in captured_roles:
|
||||
|
@ -20,6 +20,7 @@ MAX_STREAMED_SIZE = 1024 * 1024 * 1024
|
||||
@tornado.web.stream_request_body
|
||||
class UploadHandler(tornado.web.RequestHandler):
|
||||
|
||||
# noinspection PyAttributeOutsideInit
|
||||
def initialize(self, controller: Controller=None, tasks_manager=None, translator=None):
|
||||
self.controller = controller
|
||||
self.tasks_manager = tasks_manager
|
||||
@ -27,8 +28,19 @@ class UploadHandler(tornado.web.RequestHandler):
|
||||
|
||||
def prepare(self):
|
||||
self.do_upload = True
|
||||
user_data = json.loads(self.get_secure_cookie('user_data'))
|
||||
user_id = user_data['user_id']
|
||||
api_key, token_data, exec_user = self.current_user
|
||||
superuser = exec_user['superuser']
|
||||
if api_key is not None:
|
||||
superuser = superuser and api_key.superuser
|
||||
user_id = exec_user['user_id']
|
||||
|
||||
if superuser:
|
||||
exec_user_crafty_permissions = self.controller.crafty_perms.list_defined_crafty_permissions()
|
||||
elif api_key is not None:
|
||||
exec_user_crafty_permissions = self.controller.crafty_perms.get_api_key_permissions_list(api_key)
|
||||
else:
|
||||
exec_user_crafty_permissions = self.controller.crafty_perms.get_crafty_permissions_list(
|
||||
exec_user["user_id"])
|
||||
|
||||
server_id = self.request.headers.get('X-ServerId', None)
|
||||
|
||||
@ -42,8 +54,7 @@ class UploadHandler(tornado.web.RequestHandler):
|
||||
console.warning('Server ID not found in upload handler call')
|
||||
self.do_upload = False
|
||||
|
||||
user_permissions = self.controller.server_perms.get_user_permissions_list(user_id, server_id)
|
||||
if Enum_Permissions_Server.Files not in user_permissions:
|
||||
if Enum_Permissions_Server.Files not in exec_user_crafty_permissions:
|
||||
logger.warning(f'User {user_id} tried to upload a file to {server_id} without permissions!')
|
||||
console.warning(f'User {user_id} tried to upload a file to {server_id} without permissions!')
|
||||
self.do_upload = False
|
||||
|
@ -5,6 +5,7 @@ import sys
|
||||
|
||||
from urllib.parse import parse_qsl
|
||||
from app.classes.models.users import Users
|
||||
from app.classes.shared.authentication import authentication
|
||||
from app.classes.shared.helpers import helper
|
||||
from app.classes.web.websocket_helper import websocket_helper
|
||||
from app.classes.shared.console import console
|
||||
@ -19,7 +20,14 @@ except ModuleNotFoundError as e:
|
||||
console.critical("Import Error: Unable to load {} module".format(e, e.name))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class SocketHandler(tornado.websocket.WebSocketHandler):
|
||||
page = None
|
||||
page_query_params = None
|
||||
controller = None
|
||||
tasks_manager = None
|
||||
translator = None
|
||||
io_loop = None
|
||||
|
||||
def initialize(self, controller=None, tasks_manager=None, translator=None):
|
||||
self.controller = controller
|
||||
@ -34,24 +42,11 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
|
||||
return remote_ip
|
||||
|
||||
def get_user_id(self):
|
||||
user_data_cookie_raw = self.get_secure_cookie('user_data')
|
||||
|
||||
if user_data_cookie_raw and user_data_cookie_raw.decode('utf-8'):
|
||||
user_data_cookie = user_data_cookie_raw.decode('utf-8')
|
||||
user_id = json.loads(user_data_cookie)['user_id']
|
||||
return user_id
|
||||
_, _, user = authentication.check(self.get_cookie('token'))
|
||||
return user.user_id
|
||||
|
||||
def check_auth(self):
|
||||
user_data_cookie_raw = self.get_secure_cookie('user_data')
|
||||
|
||||
if user_data_cookie_raw and user_data_cookie_raw.decode('utf-8'):
|
||||
user_data_cookie = user_data_cookie_raw.decode('utf-8')
|
||||
user_id = json.loads(user_data_cookie)['user_id']
|
||||
query = Users.select().where(Users.user_id == user_id)
|
||||
if query.exists():
|
||||
return True
|
||||
return False
|
||||
|
||||
return authentication.check_bool(self.get_cookie('token'))
|
||||
|
||||
def open(self):
|
||||
logger.debug('Checking WebSocket authentication')
|
||||
@ -74,10 +69,11 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
|
||||
logger.debug('Opened WebSocket connection')
|
||||
# websocket_helper.broadcast('notification', 'New client connected')
|
||||
|
||||
def on_message(self, rawMessage):
|
||||
@staticmethod
|
||||
def on_message(raw_message):
|
||||
|
||||
logger.debug('Got message from WebSocket connection {}'.format(rawMessage))
|
||||
message = json.loads(rawMessage)
|
||||
logger.debug('Got message from WebSocket connection {}'.format(raw_message))
|
||||
message = json.loads(raw_message)
|
||||
logger.debug('Event Type: {}, Data: {}'.format(message['event'], message['data']))
|
||||
|
||||
def on_close(self):
|
||||
|
@ -5,6 +5,7 @@
|
||||
"language": "en_EN",
|
||||
"cookie_expire": 30,
|
||||
"cookie_secret": "random",
|
||||
"apikey_secret": "random",
|
||||
"show_errors": true,
|
||||
"history_max_age": 7,
|
||||
"stats_update_frequency": 30,
|
||||
|
@ -18,19 +18,22 @@
|
||||
|
||||
<li class="nav-item dropdown user-dropdown">
|
||||
<a class="nav-link dropdown-toggle" id="UserDropdown" href="#" data-toggle="dropdown" aria-expanded="false">
|
||||
<img class="img-xs rounded-circle profile-picture" src="{{ data['user_data']['profile_url'] }}" alt="Profile image"> </a>
|
||||
<img class="img-xs rounded-circle profile-picture" src="{{ data['user_data'].get('profile_url') }}" alt="Profile image"> </a>
|
||||
<div class="dropdown-menu dropdown-menu-right navbar-dropdown" aria-labelledby="UserDropdown">
|
||||
<div class="dropdown-header text-center">
|
||||
<img class="img-md rounded-circle profile-picture" src="{{ data['user_data']['profile_url'] }}" alt="Profile image">
|
||||
<img class="img-md rounded-circle profile-picture" src="{{ data['user_data'].get('profile_url') }}" alt="Profile image">
|
||||
<p class="mb-1 mt-3 font-weight-semibold">{{ data['user_data']['username'] }}</p>
|
||||
<p class="font-weight-light text-muted mb-0">Roles: </p>
|
||||
{% for r in data['user_role'] %}
|
||||
<p class="font-weight-light text-muted mb-0">{{ r }}</p>
|
||||
{% end %}
|
||||
{% if data['api_key'] %}
|
||||
<p class="mt-3">Logged in as API key "{{ data['api_key']['name'] }}"</p>
|
||||
{% end %}
|
||||
<p class="font-weight-light text-muted mb-0">Email: {{ data['user_data']['email'] }}</p>
|
||||
</div>
|
||||
<a class="dropdown-item" href="/panel/support_logs"><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i> Support Logs</i></a>
|
||||
{% if "Super User" in data['user_role'] %}
|
||||
{% if data['superuser'] %}
|
||||
<a class="dropdown-item" href="/panel/activity_logs"><i class="dropdown-item-icon mdi mdi-calendar-check-outline text-primary"></i> Activity</a>
|
||||
{% end %}
|
||||
<a class="dropdown-item" href="/public/logout"><i class="dropdown-item-icon mdi mdi-power text-primary"></i>Sign Out</a>
|
||||
|
@ -42,7 +42,6 @@
|
||||
<tr class="rounded">
|
||||
<th>User</th>
|
||||
<th>Enabled</th>
|
||||
<th>API Token</th>
|
||||
<th>Allowed Servers</th>
|
||||
<th>Assigned Roles</th>
|
||||
<th>Edit</th>
|
||||
@ -64,9 +63,6 @@
|
||||
|
||||
{% end %}
|
||||
</td>
|
||||
<td>
|
||||
<button data-toggle="tooltip" title="Show API Key" data-id="{{ user.api_token }}" type="button" class="btn btn-info show_button">Show</button>
|
||||
</td>
|
||||
<td id="server_list_{{user.user_id}}">
|
||||
<ul id="{{user.user_id}}">
|
||||
{% for item in data['auth-servers'][user.user_id] %}
|
||||
|
@ -229,7 +229,6 @@
|
||||
|
||||
$( document ).ready(function() {
|
||||
console.log( "ready!" );
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
@ -44,6 +44,10 @@
|
||||
<i class="fas fa-cogs"></i>Config</a>
|
||||
</li>
|
||||
{% if not data['new_user'] %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/edit_user_apikeys?id={{ data['user']['user_id'] }}" role="tab" aria-selected="false">
|
||||
<i class="fas fa-key"></i>API Keys</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/add_user?id={{ data['user']['user_id'] }}&subpage=other" role="tab" aria-selected="false">
|
||||
<i class="fas fa-folder-tree"></i>Other</a>
|
||||
@ -177,14 +181,6 @@
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
<label for="regen_api" class="form-check-label ml-4 mb-4">
|
||||
{% if data['new_user'] %}
|
||||
<input type="checkbox" class="form-check-input" id="regen_api" name="regen_api" checked="" value="1" disabled >Regenerate API Key
|
||||
{% else %}
|
||||
<input type="checkbox" class="form-check-input" id="regen_api" name="regen_api" value="1">Regenerate API Key
|
||||
{% end %}
|
||||
</label>
|
||||
|
||||
<label for="superuser" class="form-check-label ml-4 mb-4">
|
||||
{% if data['user']['superuser'] %}
|
||||
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser" name="superuser" checked="" value="1" {{ data['super-disabled'] }} >Super User
|
||||
@ -215,7 +211,7 @@
|
||||
<br />
|
||||
Last IP: {{ data['user']['last_ip'] }}
|
||||
<br />
|
||||
API Key: {{ data['user']['api_token'] }}
|
||||
API Key: TODO! <!-- TODO -->
|
||||
<br />
|
||||
</p>
|
||||
</blockquote>
|
||||
|
254
app/frontend/templates/panel/panel_edit_user_apikeys.html
Normal file
254
app/frontend/templates/panel/panel_edit_user_apikeys.html
Normal file
@ -0,0 +1,254 @@
|
||||
{% extends ../base.html %}
|
||||
|
||||
{% block meta %}
|
||||
<!-- <meta http-equiv="refresh" content="60">-->
|
||||
{% end %}
|
||||
|
||||
{% block title %}Crafty Controller - Edit User API Keys{% end %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Page Title Header Starts-->
|
||||
<div class="row page-title-header">
|
||||
<div class="col-12">
|
||||
<div class="page-header">
|
||||
<h4 class="page-title">
|
||||
Edit User API Keys - {{ data['user']['user_id'] }}
|
||||
<br/>
|
||||
<small>UID: {{ data['user']['user_id'] }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Page Title Header Ends-->
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-12 grid-margin">
|
||||
<div class="card">
|
||||
<div class="card-body pt-0">
|
||||
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/panel/edit_user?id={{ data['user']['user_id'] }}&subpage=config"
|
||||
role="tab"
|
||||
aria-selected="false">
|
||||
<i class="fas fa-cogs"></i>Config</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/panel/edit_user_apikeys?id={{ data['user']['user_id'] }}"
|
||||
role="tab"
|
||||
aria-selected="true">
|
||||
<i class="fas fa-key"></i>API Keys</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||
<h4 class="card-title"><i class="fas fa-key"></i> API Keys</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<!--<th>ID</th>-->
|
||||
<th>Name</th>
|
||||
<th>Created</th>
|
||||
<th>Superuser</th>
|
||||
<th>Permissions</th>
|
||||
<th>Buttons</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for apikey in data['api_keys'] %}
|
||||
<tr>
|
||||
<!--<td>{-{ apikey.token_id }-}</td>-->
|
||||
<td>{{ apikey.name }}</td>
|
||||
<td>{{ apikey.created.strftime('%d/%m/%Y %H:%M:%S') }}</td>
|
||||
<td>
|
||||
{% if apikey.superuser %}
|
||||
<span class="text-success">
|
||||
<i class="fas fa-check-square"></i> Yes
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-danger">
|
||||
<i class="far fa-times-square"></i> No
|
||||
</span>
|
||||
{% end %}
|
||||
</td>
|
||||
<td>Server: {{ apikey.server_permissions }}
|
||||
Crafty: {{ apikey.crafty_permissions }}</td>
|
||||
<td>
|
||||
<button
|
||||
class="btn btn-danger delete-api-key"
|
||||
data-key-id="{{ apikey.token_id }}"
|
||||
data-key-name="{{ apikey.name }}"
|
||||
>{{ translate('panelConfig', 'delete', data['lang']) }}</button>
|
||||
<button
|
||||
class="btn btn-outline-primary get-a-token"
|
||||
data-key-id="{{ apikey.token_id }}"
|
||||
data-key-name="{{ apikey.name }}"
|
||||
>Get a token
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-5 col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header header-sm d-flex justify-content-between align-items-center">
|
||||
<h4 class="card-title"><i class="fas fa-plus"></i> Create new API key</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="user_form" class="forms-sample" method="post"
|
||||
action="/panel/edit_user_apikeys">
|
||||
{% raw xsrf_form_html() %}
|
||||
<input type="hidden" name="id" value="{{ data['user']['user_id'] }}">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">Name <small
|
||||
class="text-muted ml-1"> - What you wish to
|
||||
call this API key</small> </label>
|
||||
<input type="text" class="form-control" name="name" id="name"
|
||||
placeholder="API Key">
|
||||
</div>
|
||||
|
||||
<table class="table table-hover mb-3">
|
||||
<thead>
|
||||
<tr class="rounded">
|
||||
<th>Permission Name</th>
|
||||
<th>Authorized ?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for permission in data['server_permissions_all'] %}
|
||||
<tr>
|
||||
<td><label
|
||||
for="permission_{{ permission.name }}">{{ permission.name }}</label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" class=""
|
||||
id="permission_{{ permission.name }}"
|
||||
name="permission_{{ permission.name }}" value="1">
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
{% for permission in data['crafty_permissions_all'] %}
|
||||
<tr>
|
||||
<td><label
|
||||
for="permission_{{ permission.name }}">{{ permission.name }}</label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" class=""
|
||||
id="permission_{{ permission.name }}"
|
||||
name="permission_{{ permission.name }}" value="1">
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<label for="superuser">Superuser</label>
|
||||
<input type="checkbox" class="" id="superuser"
|
||||
name="superuser" value="1">
|
||||
|
||||
<br/>
|
||||
|
||||
<button type="submit" class="btn btn-success mr-2"><i class="fas fa-plus"></i>
|
||||
Create
|
||||
</button>
|
||||
<button type="reset" class="btn btn-light"><i
|
||||
class="fas fa-undo-alt"></i> {{ translate('panelConfig', 'cancel', data['lang']) }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- content-wrapper ends -->
|
||||
|
||||
{% end %}
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
|
||||
|
||||
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
||||
function getCookie(name) {
|
||||
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
|
||||
return r ? r[1] : undefined;
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log("ready!");
|
||||
$('.delete-api-key').click(function () {
|
||||
var keyId = $(this).data("key-id");
|
||||
var keyName = $(this).data("key-name");
|
||||
bootbox.confirm({
|
||||
title: `Remove API key ${keyName}?`,
|
||||
message: "Do you want to delete this API key? This cannot be undone.",
|
||||
buttons: {
|
||||
cancel: {
|
||||
label: '<i class="fas fa-times"></i> {{ translate("panelConfig", "cancel", data['lang']) }}'
|
||||
},
|
||||
confirm: {
|
||||
label: '<i class="fas fa-check"></i> {{ translate("serverBackups", "confirm", data['lang']) }}'
|
||||
}
|
||||
},
|
||||
callback: function (result) {
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/panel/remove_apikey?id=' + keyId,
|
||||
success: function (data) {
|
||||
location.reload();
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
$('.get-a-token').click(function () {
|
||||
var keyId = $(this).data("key-id");
|
||||
var keyName = $(this).data("key-name");
|
||||
var token = getCookie("_xsrf")
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
headers: {'X-XSRFToken': token},
|
||||
url: '/panel/get_token?id=' + keyId,
|
||||
success: function (data) {
|
||||
bootbox.alert({
|
||||
title: `API token for ${keyName}`,
|
||||
message: `Here is an API token for ${keyName}:\n<pre style="white-space: pre-wrap;color:white;word-break:break-all;background: grey;border-radius: 5px;">${data}</pre>`
|
||||
});
|
||||
},
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
{% end %}
|
12
app/migrations/20211120221511_api_keys.py
Normal file
12
app/migrations/20211120221511_api_keys.py
Normal file
@ -0,0 +1,12 @@
|
||||
import peewee
|
||||
import datetime
|
||||
|
||||
|
||||
def migrate(migrator, database, **kwargs):
|
||||
migrator.add_columns('users', valid_tokens_from=peewee.DateTimeField(default=datetime.datetime.now))
|
||||
migrator.drop_columns('users', ['api_token'])
|
||||
|
||||
|
||||
def rollback(migrator, database, **kwargs):
|
||||
migrator.drop_columns('users', ['valid_tokens_from'])
|
||||
migrator.add_columns('users', api_token=peewee.CharField(default="", unique=True, index=True))
|
23
app/migrations/20211121233959_multi_api_keys.py
Normal file
23
app/migrations/20211121233959_multi_api_keys.py
Normal file
@ -0,0 +1,23 @@
|
||||
import peewee
|
||||
import datetime
|
||||
from app.classes.models.users import Users
|
||||
|
||||
|
||||
def migrate(migrator, db):
|
||||
class ApiKeys(peewee.Model):
|
||||
token_id = peewee.AutoField()
|
||||
name = peewee.CharField(default='', unique=True, index=True)
|
||||
created = peewee.DateTimeField(default=datetime.datetime.now)
|
||||
user = peewee.ForeignKeyField(Users, backref='api_token', index=True)
|
||||
server_permissions = peewee.CharField(default='00000000')
|
||||
crafty_permissions = peewee.CharField(default='000')
|
||||
superuser = peewee.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
table_name = 'api_keys'
|
||||
|
||||
migrator.create_table(ApiKeys)
|
||||
|
||||
|
||||
def rollback(migrator, db):
|
||||
migrator.drop_table('api_keys')
|
@ -370,5 +370,10 @@
|
||||
},
|
||||
"base": {
|
||||
"doesNotWorkWithoutJavascript": "<strong>Warning: </strong>Crafty doesn't work properly when JavaScript isn't enabled!"
|
||||
},
|
||||
"apiKeys": {
|
||||
"deleteKeyConfirmation": "Do you want to delete this API key? This cannot be undone.",
|
||||
"deleteKeyConfirmationTitle": "Remove API key ${keyId}?"
|
||||
}
|
||||
|
||||
}
|
@ -371,5 +371,9 @@
|
||||
},
|
||||
"base": {
|
||||
"doesNotWorkWithoutJavascript": "<strong>Varoitus: </strong>Crafty ei toimi kunnolla ilman JavaScriptiä!"
|
||||
},
|
||||
"apiKeys": {
|
||||
"deleteKeyConfirmation": "Haluatko varmasti poistaa tämän API-avaimen? Tämä on peruuttamaton toimenpide!",
|
||||
"deleteKeyConfirmationTitle": "Poistetaanko API-avain ${keyId}?"
|
||||
}
|
||||
}
|
@ -346,5 +346,9 @@
|
||||
},
|
||||
"base": {
|
||||
"doesNotWorkWithoutJavascript": "<strong>Attention: </strong>Crafty ne fonctionne pas correctement si JavaScript n'est pas activé !"
|
||||
},
|
||||
"apiKeys": {
|
||||
"deleteKeyConfirmation": "Es-tu sûr de vouloir supprimer cette clé API? Tu ne pourras plus revenir en arrière.",
|
||||
"deleteKeyConfirmationTitle": "Supprimer la clé API ${keyId}?"
|
||||
}
|
||||
}
|
@ -16,3 +16,4 @@ cached_property==1.5.2
|
||||
apscheduler~=3.8.1
|
||||
cron-validator~=1.0.3
|
||||
tzlocal~=4.1
|
||||
pyjwt~=2.3
|
||||
|
Loading…
Reference in New Issue
Block a user