Aidaho 2024-09-16 10:56:24 +03:00
parent 71ed2cc756
commit 85d745f7dd
20 changed files with 213 additions and 191 deletions

View File

@ -32,7 +32,7 @@ def before_request():
def register_api(view, endpoint, url, pk='listener_id', pk_type='int'):
view_func = view.as_view(endpoint)
view_func = view.as_view(endpoint, True)
bp.add_url_rule(url, view_func=view_func, methods=['POST'])
bp.add_url_rule(f'{url}/<{pk_type}:{pk}>', view_func=view_func, methods=['GET', 'PUT', 'PATCH', 'DELETE'])

View File

@ -469,30 +469,6 @@ def update_db_v_4_3_0():
print("An error occurred:", e)
def update_db_v_7_1_2():
try:
Setting.delete().where(Setting.param == 'stats_user').execute()
Setting.delete().where(Setting.param == 'stats_password').execute()
Setting.delete().where(Setting.param == 'stats_port').execute()
Setting.delete().where(Setting.param == 'stats_page').execute()
except Exception as e:
print("An error occurred:", e)
else:
print("Updating... DB has been updated to version 7.1.2")
def update_db_v_7_1_2_1():
try:
migrate(
migrator.add_column('cred', 'passphrase', CharField(null=True))
)
except Exception as e:
if e.args[0] == 'duplicate column name: passphrase' or str(e) == '(1060, "Duplicate column name \'passphrase\'")':
print('Updating... DB has been updated to version 7.1.2-1')
else:
print("An error occurred:", e)
def update_db_v_7_2_0():
try:
if mysql_enable:
@ -620,9 +596,26 @@ def update_db_v_8():
print("An error occurred:", e)
def update_db_v_8_0_2():
try:
if mysql_enable:
migrate(
migrator.add_column('cred', 'shared', IntegerField(default=0)),
)
else:
migrate(
migrator.add_column('cred', 'shared', IntegerField(constraints=[SQL('DEFAULT 0')])),
)
except Exception as e:
if e.args[0] == 'duplicate column name: shared' or str(e) == '(1060, "Duplicate column name \'shared\'")':
print('Updating... DB has been updated to version 8.0.2')
else:
print("An error occurred:", e)
def update_ver():
try:
Version.update(version='8.0.1').execute()
Version.update(version='8.0.2').execute()
except Exception:
print('Cannot update version')
@ -640,12 +633,11 @@ def update_all():
if check_ver() is None:
update_db_v_3_4_5_22()
update_db_v_4_3_0()
update_db_v_7_1_2()
update_db_v_7_1_2_1()
update_db_v_7_2_0()
update_db_v_7_2_0_1()
update_db_v_7_2_3()
update_db_v_7_3_1()
update_db_v_7_4()
update_db_v_8()
update_db_v_8_0_2()
update_ver()

View File

@ -2,6 +2,8 @@ import os
import sys
import traceback
from app.modules.roxywi.exception import RoxywiConflictError
def out_error(error):
error = str(error)
@ -11,3 +13,11 @@ def out_error(error):
function_name = stk[0][2]
error = f'{error} in function: {function_name} in file: {file_name}'
raise Exception(f'error: {error}')
def not_unique_error(error):
if error.args[0] == 1062:
col = error.args[1].split('key ')[1]
else:
col = error.args[0].split(': ')[-1]
raise RoxywiConflictError(f'{col} must be uniq')

View File

@ -4,8 +4,15 @@ from app.modules.roxywi.exception import RoxywiResourceNotFound
def select_ssh(**kwargs):
if kwargs.get("group") and kwargs.get("cred_id"):
query = Cred.select().where((Cred.id == kwargs.get('cred_id')) & (Cred.group_id == kwargs.get('group')))
if kwargs.get("group") and kwargs.get("cred_id") and kwargs.get("not_shared"):
query = Cred.select().where(
((Cred.id == kwargs.get('cred_id')) & (Cred.group_id == kwargs.get('group')))
)
elif kwargs.get("group") and kwargs.get("cred_id"):
query = Cred.select().where(
((Cred.id == kwargs.get('cred_id')) & (Cred.group_id == kwargs.get('group'))) |
(Cred.shared == 1)
)
elif kwargs.get("name") is not None:
query = Cred.select().where(Cred.name == kwargs.get('name'))
elif kwargs.get("id") is not None:
@ -13,7 +20,7 @@ def select_ssh(**kwargs):
elif kwargs.get("serv") is not None:
query = Cred.select().join(Server, on=(Cred.id == Server.cred_id)).where(Server.ip == kwargs.get('serv'))
elif kwargs.get("group") is not None:
query = Cred.select().where(Cred.group_id == kwargs.get("group"))
query = Cred.select().where((Cred.group_id == kwargs.get("group")) | (Cred.shared == 1))
else:
query = Cred.select()
try:
@ -26,11 +33,11 @@ def select_ssh(**kwargs):
return query_res
def insert_new_ssh(name, enable, group, username, password):
def insert_new_ssh(name, enable, group, username, password, shared):
if password is None:
password = 'None'
try:
return Cred.insert(name=name, key_enabled=enable, group_id=group, username=username, password=password).execute()
return Cred.insert(name=name, key_enabled=enable, group_id=group, username=username, password=password, shared=shared).execute()
except Exception as e:
out_error(e)
@ -45,21 +52,22 @@ def delete_ssh(ssh_id):
return True
def update_ssh(cred_id, name, enable, group, username, password):
def update_ssh(cred_id: int, name: str, enable: int, group: int, username: str, password: bytes, shared: int):
if password is None:
password = 'None'
cred_update = Cred.update(name=name, key_enabled=enable, group_id=group, username=username, password=password).where(
Cred.id == cred_id)
cred_update = Cred.update(
name=name, key_enabled=enable, group_id=group, username=username, password=password, shared=shared
).where(Cred.id == cred_id)
try:
cred_update.execute()
except Exception as e:
out_error(e)
def update_ssh_passphrase(name: str, passphrase: str):
def update_ssh_passphrase(cred_id: int, passphrase: str):
try:
Cred.update(passphrase=passphrase).where(Cred.name == name).execute()
Cred.update(passphrase=passphrase).where(Cred.id == cred_id).execute()
except Exception as e:
out_error(e)

View File

@ -196,6 +196,7 @@ class Cred(BaseModel):
password = CharField(null=True)
group_id = IntegerField(constraints=[SQL('DEFAULT 1')])
passphrase = CharField(null=True)
shared = IntegerField(constraints=[SQL('DEFAULT 0')])
class Meta:
table_name = 'cred'

View File

@ -1,5 +1,7 @@
from peewee import IntegrityError
from app.modules.db.db_model import mysql_enable, connect, Server, SystemInfo
from app.modules.db.common import out_error
from app.modules.db.common import out_error, not_unique_error
from app.modules.roxywi.exception import RoxywiResourceNotFound
@ -10,9 +12,10 @@ def add_server(hostname, ip, group, type_ip, enable, master, cred, port, desc, h
port=port, description=desc, haproxy=haproxy, nginx=nginx, apache=apache, firewall_enable=firewall
).execute()
return server_id
except IntegrityError as e:
not_unique_error(e)
except Exception as e:
out_error(e)
return False
def delete_server(server_id):

View File

@ -139,6 +139,7 @@ class CredRequest(BaseModel):
password: Optional[EscapedString] = None
key_enabled: Optional[bool] = 1
group_id: Optional[int] = None
shared: Optional[int] = 0
class CredUploadRequest(BaseModel):

View File

@ -16,7 +16,8 @@ import app.modules.db.history as history_sql
import app.modules.db.ha_cluster as ha_sql
import app.modules.roxy_wi_tools as roxy_wi_tools
from app.modules.roxywi.class_models import ErrorResponse
from app.modules.roxywi.exception import RoxywiResourceNotFound, RoxywiGroupMismatch, RoxywiGroupNotFound
from app.modules.roxywi.exception import RoxywiResourceNotFound, RoxywiGroupMismatch, RoxywiGroupNotFound, \
RoxywiPermissionError
get_config_var = roxy_wi_tools.GetConfigVar()
@ -334,5 +335,7 @@ def handler_exceptions_for_json_data(ex: Exception, main_ex_mes: str) -> tuple[d
return handle_json_exceptions(ex, 'Group not found'), 404
elif isinstance(ex, RoxywiGroupMismatch):
return handle_json_exceptions(ex, 'Resource not found in group'), 404
elif isinstance(ex, RoxywiPermissionError):
return handle_json_exceptions(ex, 'You cannot edit this resource'), 403
else:
return handle_json_exceptions(ex, main_ex_mes), 500

View File

@ -31,3 +31,17 @@ class RoxywiValidationError(Exception):
def __init__(self, message='Validation error'):
super(RoxywiValidationError, self).__init__(message)
class RoxywiPermissionError(Exception):
""" This class represents an exception raised when a permission error occurs. """
def __init__(self, message='Forbidden'):
super(RoxywiPermissionError, self).__init__(message)
class RoxywiConflictError(Exception):
""" This class represents an exception raised when a conflict error occurs."""
def __init__(self, message='Conflict'):
super(RoxywiConflictError, self).__init__(message)

View File

@ -13,14 +13,13 @@ import app.modules.common.common as common
from app.modules.server import ssh_connection
import app.modules.roxywi.common as roxywi_common
import app.modules.roxy_wi_tools as roxy_wi_tools
from app.modules.roxywi.class_models import IdResponse, IdDataResponse
from app.modules.roxywi.class_models import IdResponse, IdDataResponse, CredRequest
error_mess = common.error_mess
get_config = roxy_wi_tools.GetConfigVar()
def return_ssh_keys_path(server_ip: str, **kwargs) -> dict:
lib_path = get_config.get_config_var('main', 'lib_path')
ssh_settings = {}
if kwargs.get('id'):
sshs = cred_sql.select_ssh(id=kwargs.get('id'))
@ -43,10 +42,10 @@ def return_ssh_keys_path(server_ip: str, **kwargs) -> dict:
else:
passphrase = ssh.passphrase
ssh_key = _return_correct_ssh_file(ssh)
ssh_settings.setdefault('enabled', ssh.key_enabled)
ssh_settings.setdefault('user', ssh.username)
ssh_settings.setdefault('password', password)
ssh_key = f'{lib_path}/keys/{ssh.name}.pem' if ssh.key_enabled == 1 else ''
ssh_settings.setdefault('key', ssh_key)
ssh_settings.setdefault('passphrase', passphrase)
@ -66,10 +65,8 @@ def ssh_connect(server_ip):
return ssh
def create_ssh_cred(name: str, password: str, group: int, username: str, enable: int, is_api: int) -> dict:
group_name = group_sql.get_group_name_by_id(group)
def create_ssh_cred(name: str, password: str, group: int, username: str, enable: int, is_api: int, shared: int) -> dict:
lang = roxywi_common.get_user_lang_for_flask()
name = f'{name}_{group_name}'
if password and password != "''":
try:
password = crypt_password(password)
@ -79,43 +76,20 @@ def create_ssh_cred(name: str, password: str, group: int, username: str, enable:
password = ''
try:
last_id = cred_sql.insert_new_ssh(name, enable, group, username, password)
last_id = cred_sql.insert_new_ssh(name, enable, group, username, password, shared)
except Exception as e:
return roxywi_common.handle_json_exceptions(e, 'Cannot create new SSH credentials')
roxywi_common.logging('RMON server', f'New SSH credentials {name} has been created', roxywi=1, login=1)
roxywi_common.logging('Roxy-WI server', f'New SSH credentials {name} has been created', roxywi=1, login=1)
if is_api:
return IdResponse(id=last_id).model_dump(mode='json')
else:
data = render_template('ajax/new_ssh.html', groups=group_sql.select_groups(), sshs=cred_sql.select_ssh(name=name), lang=lang)
data = render_template('ajax/new_ssh.html',
groups=group_sql.select_groups(), sshs=cred_sql.select_ssh(name=name), lang=lang, adding=1
)
return IdDataResponse(id=last_id, data=data).model_dump(mode='json')
def create_ssh_cred_api(name: str, enable: str, group: str, username: str, password: str) -> bool:
group_name = group_sql.get_group_name_by_id(group)
name = common.checkAjaxInput(name)
name = f'{name}_{group_name}'
enable = common.checkAjaxInput(enable)
username = common.checkAjaxInput(username)
password = common.checkAjaxInput(password)
if password != '':
try:
password = crypt_password(password)
except Exception as e:
raise Exception(e)
if username is None or name is None:
return False
else:
try:
cred_sql.insert_new_ssh(name, enable, group, username, password)
roxywi_common.logging('Roxy-WI server', f'New SSH credentials {name} has been created', roxywi=1)
return True
except Exception as e:
roxywi_common.handle_exceptions(e, 'Roxy-WI server', f'Cannot create SSH credentials {name}', roxywi=1)
def upload_ssh_key(ssh_id: int, key: str, passphrase: str) -> None:
key = key.replace("'", "")
ssh = cred_sql.get_ssh(ssh_id)
@ -123,37 +97,23 @@ def upload_ssh_key(ssh_id: int, key: str, passphrase: str) -> None:
lib_path = get_config.get_config_var('main', 'lib_path')
full_dir = f'{lib_path}/keys/'
name = ssh.name
ssh_keys = f'{name}.pem'
ssh_keys = f'{full_dir}{name}_{group_name}.pem'
try:
key = paramiko.pkey.load_private_key(key, password=passphrase)
except Exception as e:
raise Exception(e)
if key != '':
try:
key = paramiko.pkey.load_private_key(key, password=passphrase)
except Exception as e:
raise Exception(e)
try:
_ = name.split('_')[1]
split_name = True
except Exception:
split_name = False
try:
key.write_private_key_file(ssh_keys)
except Exception as e:
raise Exception(e)
if not os.path.isfile(ssh_keys) and not split_name:
name = f'{ssh.name}_{group_name}'
if not os.path.exists(full_dir):
os.makedirs(full_dir)
ssh_keys = f'{full_dir}{name}.pem'
try:
key.write_private_key_file(ssh_keys)
except Exception as e:
raise Exception(e)
try:
os.chmod(ssh_keys, 0o600)
except IOError as e:
roxywi_common.logging('RMON server', e.args[0], roxywi=1)
raise Exception(e)
try:
os.chmod(ssh_keys, 0o600)
except IOError as e:
raise Exception(e)
if passphrase:
try:
@ -168,54 +128,48 @@ def upload_ssh_key(ssh_id: int, key: str, passphrase: str) -> None:
except Exception as e:
raise Exception(e)
roxywi_common.logging("RMON server", f"A new SSH cert has been uploaded {ssh_keys}", roxywi=1, login=1)
roxywi_common.logging("Roxy-WI server", f"A new SSH cert has been uploaded {ssh_keys}", roxywi=1, login=1)
def update_ssh_key(ssh_id: int, name: str, password: str, enable: int, username: str, group: int) -> None:
lib_path = get_config.get_config_var('main', 'lib_path')
def update_ssh_key(body: CredRequest, group_id: int, ssh_id: int) -> None:
ssh = cred_sql.get_ssh(ssh_id)
ssh_key_name = f'{lib_path}/keys/{ssh.name}.pem'
new_ssh_key_name = f'{lib_path}/keys/{name}.pem'
ssh_key_name = _return_correct_ssh_file(ssh)
if password != '':
if body.password != '' and body.password is not None:
try:
password = crypt_password(password)
body.password = crypt_password(body.password)
except Exception as e:
raise Exception(e)
if ssh.key_enabled == 1 and os.path.isfile(ssh_key_name):
if os.path.isfile(ssh_key_name):
new_ssh_key_name = _return_correct_ssh_file(body)
os.rename(ssh_key_name, new_ssh_key_name)
os.chmod(new_ssh_key_name, 0o600)
try:
cred_sql.update_ssh(ssh_id, name, enable, group, username, password)
roxywi_common.logging('RMON server', f'The SSH credentials {name} has been updated ', roxywi=1, login=1)
cred_sql.update_ssh(ssh_id, body.name, body.key_enabled, group_id, body.username, body.password, body.shared)
roxywi_common.logging('Roxy-WI server', f'The SSH credentials {body.name} has been updated ', roxywi=1, login=1)
except Exception as e:
raise Exception(e)
def delete_ssh_key(ssh_id) -> str:
lib_path = get_config.get_config_var('main', 'lib_path')
def delete_ssh_key(ssh_id) -> None:
name = ''
ssh_enable = 0
ssh_key_name = ''
for sshs in cred_sql.select_ssh(id=ssh_id):
ssh_enable = sshs.key_enabled
name = sshs.name
ssh_key_name = f'{lib_path}/keys/{sshs.name}.pem'
if ssh_enable == 1:
try:
os.remove(ssh_key_name)
except Exception:
pass
if sshs.key_enabled == 1:
ssh_key_name = _return_correct_ssh_file(sshs)
try:
os.remove(ssh_key_name)
except Exception:
pass
try:
cred_sql.delete_ssh(ssh_id)
roxywi_common.logging('Roxy-WI server', f'The SSH credentials {name} has deleted', roxywi=1, login=1)
return 'ok'
except Exception as e:
roxywi_common.handle_exceptions(e, 'Roxy-WI server', f'Cannot delete SSH credentials {name}', roxywi=1, login=1)
raise e
def crypt_password(password: str) -> bytes:
@ -248,12 +202,11 @@ def decrypt_password(password: str) -> str:
return decryp_pass
def get_creds(group_id: int = None, cred_id: int = None) -> list:
def get_creds(group_id: int = None, cred_id: int = None, not_shared: bool = False) -> list:
json_data = []
lib_path = get_config.get_config_var('main', 'lib_path')
if group_id and cred_id:
creds = cred_sql.select_ssh(group=group_id, cred_id=cred_id)
creds = cred_sql.select_ssh(group=group_id, cred_id=cred_id, not_shared=not_shared)
elif group_id:
creds = cred_sql.select_ssh(group=group_id)
else:
@ -262,15 +215,33 @@ def get_creds(group_id: int = None, cred_id: int = None) -> list:
for cred in creds:
cred_dict = model_to_dict(cred)
cred_dict['name'] = cred_dict['name'].replace("'", "")
ssh_key_file = f'{lib_path}/keys/{cred_dict["name"]}.pem'
if os.path.isfile(ssh_key_file):
with open(ssh_key_file, 'rb') as key:
cred_dict['private_key'] = base64.b64encode(key.read()).decode('utf-8')
if cred.key_enabled == 1:
ssh_key_file = _return_correct_ssh_file(cred)
if os.path.isfile(ssh_key_file):
with open(ssh_key_file, 'rb') as key:
cred_dict['private_key'] = base64.b64encode(key.read()).decode('utf-8')
else:
cred_dict['private_key'] = ''
else:
cred_dict['private_key'] = ''
if cred_dict['password']:
cred_dict['password'] = decrypt_password(cred_dict['password'])
try:
cred_dict['password'] = decrypt_password(cred_dict['password'])
except Exception:
pass
if cred_dict['passphrase']:
cred_dict['passphrase'] = decrypt_password(cred_dict['passphrase'])
json_data.append(cred_dict)
return json_data
def _return_correct_ssh_file(cred: CredRequest) -> str:
lib_path = get_config.get_config_var('main', 'lib_path')
group_name = group_sql.get_group_name_by_id(cred.group_id)
# if not cred.key_enabled:
# return ''
if group_name not in cred.name:
return f'{lib_path}/keys/{cred.name}_{group_name}.pem'
else:
return f'{lib_path}/keys/{cred.name}.pem'

View File

@ -61,6 +61,10 @@ function addCreds(dialog_id) {
if ($('#new-ssh_enable').is(':checked')) {
ssh_enable = '1';
}
let ssh_shared = 0;
if ($('#new-ssh_shared').is(':checked')) {
ssh_shared = '1';
}
let valid = true;
let allFields = $([]).add(ssh_add_div).add($('#ssh_user'))
allFields.removeClass("ui-state-error");
@ -71,7 +75,8 @@ function addCreds(dialog_id) {
"name": ssh_add_div.val(),
"group_id": $('#new-sshgroup').val(),
"username": $('#ssh_user').val(),
"pass": $('#ssh_pass').val(),
"password": $('#ssh_pass').val(),
"shared": ssh_shared,
"key_enabled": ssh_enable,
}
$.ajax({
@ -113,10 +118,14 @@ function sshKeyEnableShow(id) {
function updateSSH(id) {
toastr.clear();
let ssh_enable = 0;
let ssh_shared = 0;
let ssh_name_val = $('#ssh_name-' + id).val();
if ($('#ssh_enable-' + id).is(':checked')) {
ssh_enable = '1';
}
if ($('#ssh_shared-' + id).is(':checked')) {
ssh_shared = '1';
}
let group = $('#sshgroup-' + id).val();
if (group === undefined || group === null) {
group = $('#new-sshgroup').val();
@ -127,6 +136,7 @@ function updateSSH(id) {
"key_enabled": ssh_enable,
"username": $('#ssh_user-' + id).val(),
"password": $('#ssh_pass-' + id).val(),
"shared": ssh_shared,
}
$.ajax({
url: "/server/cred/" + id,

View File

@ -221,7 +221,7 @@ function createHaClusterStep1(edited=false, cluster_id=0, clean=true) {
toastr.error('error: Wrong VIP');
return false;
}
jsonData = createJsonCluster('#enabled-check div div span');
let jsonData = createJsonCluster('#enabled-check div div span');
if (!validateSlaves(jsonData)) {
return false;
}

View File

@ -1,4 +1,6 @@
{% if adding %}
{% import 'languages/'+lang|default('en')+'.html' as lang %}
{% endif %}
{% for ssh in sshs %}
<tr style="width: 50%;" id="ssh-table-{{ssh.id}}" class="ssh-table-{{ssh.id}}">
<td class="first-collumn padding10">
@ -24,6 +26,13 @@
</select>
</td>
{% endif %}
<td class="first-collumn" valign="top" style="padding-top: 15px;">
{% if ssh.shared == 1 %}
<label for="ssh_shared-{{ssh.id}}"></label><input type="checkbox" id="ssh_shared-{{ssh.id}}" checked>
{% else %}
<label for="ssh_shared-{{ssh.id}}"></label><input type="checkbox" id="ssh_shared-{{ssh.id}}">
{% endif %}
</td>
<td style="padding-top: 15px;">
<p>
<input type="text" id="ssh_user-{{ssh.id}}" class="form-control" value="{{ssh.username.replace("'", "")}}">
@ -39,4 +48,16 @@
<a class="delete" onclick="confirmDeleteSsh({{ssh.id}})" title="{{lang.words.delete|title()}} {{ssh.name}}" style="cursor: pointer;"></a>
</td>
</tr>
{% if ssh.shared and g.user_params['group_id']|string() != ssh.group_id|string() %}
<script>
$( function() {
$('#sshgroup-{{ssh.id}}').selectmenu('disable');
$('#ssh_shared-{{ssh.id}}').checkboxradio('disable');
$('#ssh_enable-{{ssh.id}}').checkboxradio('disable');
$('#ssh_name-{{ ssh.id }}').prop('readonly', true);
$('#ssh_user-{{ ssh.id }}').prop('readonly', true);
$('#ssh_pass-{{ ssh.id }}').prop('readonly', true);
});
</script>
{% endif %}
{% endfor %}

View File

@ -11,54 +11,13 @@
{% if g.user_params['role'] == 1 %}
<td style="width: 25%;">{{lang.words.group|title()}}</td>
{% endif %}
<td>{{ lang.words.shared|title() }}</td>
<td style="width: 100%;" class="help_cursor" id="ssh-user-name-td">
<span title="Enter SSH user name. If SSH key disabled, enter password for SSH user">{{lang.words.username|title()}}</span>
</td>
<td></td>
</tr>
{% for ssh in sshs %}
<tr style="width: 50%;" id="ssh-table-{{ssh.id}}" class="{{ loop.cycle('odd', 'even') }}">
<td class="first-collumn padding10">
{% set id = 'ssh_name-' + ssh.id|string() %}
{{ input(id, value=ssh.name.replace("'", ""), size='15') }}
</td>
<td class="first-collumn" valign="top" style="padding-top: 15px;">
{% if ssh.key_enabled == 1 %}
<label for="ssh_enable-{{ssh.id}}">{{lang.words.enable|title()}} SSH {{lang.words.key}}</label><input type="checkbox" id="ssh_enable-{{ssh.id}}" checked>
{% else %}
<label for="ssh_enable-{{ssh.id}}">{{lang.words.enable|title()}} SSH {{lang.words.key}}</label><input type="checkbox" id="ssh_enable-{{ssh.id}}">
{% endif %}
</td>
{% if g.user_params['role'] == 1 %}
<td>
<select id="sshgroup-{{ssh.id}}" name="sshgroup-{{ssh.id}}">
{% for group in groups %}
{% if ssh.group_id == group.group_id %}
<option value="{{ group.group_id }}" selected>{{ group.name }}</option>
{% else %}
<option value="{{ group.group_id }}">{{ group.name }}</option>
{% endif %}
{% endfor %}
</select>
</td>
{% endif %}
<td style="padding-top: 15px;">
<p>
{% set id = 'ssh_user-' + ssh.id|string() %}
{{ input(id, value=ssh.username, title='SSH user name') }}
</p>
{% if ssh.key_enabled == 1 %}
<input type="password" id="ssh_pass-{{ssh.id}}" class="form-control" title="User password, if SSH key is disabled" value="{{ ssh.password }}" style="display: none;" autocomplete="new-password">
{% else %}
<input type="password" id="ssh_pass-{{ssh.id}}" class="form-control" title="User password, if SSH key is disabled" value="{{ ssh.password }}" autocomplete="new-password">
{% endif %}
<br>
</td>
<td>
<a class="delete" onclick="confirmDeleteSsh({{ssh.id}})" title="{{lang.words.delete|title()}} SSH {{lang.words.creds}} {{ssh.name}}" style="cursor: pointer;"></a>
</td>
</tr>
{% endfor %}
{% include 'ajax/new_ssh.html' %}
</table>
<br /><span class="add-button" title="{{lang.words.add|title()}} SSH" id="add-ssh-button">+ {{lang.words.add|title()}}</span>
<br /><br />

View File

@ -127,6 +127,12 @@
<label for="new-ssh_enable">{{lang.words.enabled|title()}} SSH {{lang.words.key}}</label><input type="checkbox" id="new-ssh_enable" checked>
</td>
</tr>
<tr>
<td class="padding20">{{lang.words.shared|title()}}</td>
<td>
<label for="new-ssh_shared"></label><input type="checkbox" id="new-ssh_shared">
</td>
</tr>
{% if g.user_params['role'] == 1 %}
<tr>
<td class="padding20">{{lang.words.group|title()}}</td>

View File

@ -952,5 +952,6 @@
"listeners": "listeners",
"weight": "weight",
"where": "where",
"shared": "shared"
}
%}

View File

@ -952,5 +952,6 @@
"listeners": "les auditeurs",
"weight": "poids",
"where": "où",
"shared": "commun"
}
%}

View File

@ -952,5 +952,6 @@
"listeners": "ouvintes",
"weight": "peso",
"where": "onde",
"shared": "partilhado"
}
%}

View File

@ -952,5 +952,6 @@
"listeners": "слушатели",
"weight": "вес",
"where": "где",
"shared": "общий"
}
%}

View File

@ -9,8 +9,9 @@ import app.modules.db.cred as cred_sql
import app.modules.roxywi.common as roxywi_common
import app.modules.server.ssh as ssh_mod
from app.middleware import get_user_params, page_for_admin, check_group
from app.modules.roxywi.exception import RoxywiGroupMismatch, RoxywiResourceNotFound
from app.modules.roxywi.class_models import BaseResponse, GroupQuery, CredRequest, CredUploadRequest
from app.modules.db.db_model import Cred
from app.modules.roxywi.exception import RoxywiGroupMismatch, RoxywiResourceNotFound, RoxywiPermissionError
from app.modules.roxywi.class_models import BaseResponse, GroupQuery, CredRequest, CredUploadRequest, ErrorResponse
from app.modules.common.common_classes import SupportClass
@ -65,12 +66,15 @@ class CredView(MethodView):
private_key:
type: 'string'
description: 'SSH private key in base64 encoded format'
shared:
type: 'integer'
description: 'Is shared credential for other groups or not'
404:
description: 'Credential not found'
"""
group_id = SupportClass.return_group_id(query)
try:
creds = ssh_mod.get_creds(group_id=group_id, cred_id=cred_id)
creds = ssh_mod.get_creds(group_id=group_id, cred_id=cred_id, not_shared=True)
return jsonify(creds), 200
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot get credentials')
@ -109,13 +113,16 @@ class CredView(MethodView):
password:
type: string
description: The password
shared:
type: 'integer'
description: 'Is shared credential for other groups or not'
responses:
201:
description: Credential addition successful
"""
group_id = SupportClass.return_group_id(body)
try:
return ssh_mod.create_ssh_cred(body.name, body.password, group_id, body.username, body.key_enabled, self.is_api)
return ssh_mod.create_ssh_cred(body.name, body.password, group_id, body.username, body.key_enabled, self.is_api, body.shared)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot create new cred')
@ -157,18 +164,24 @@ class CredView(MethodView):
password:
type: string
description: The password
shared:
type: 'integer'
description: 'Is shared credential for other groups or not'
responses:
201:
description: Credential update successful
"""
group_id = SupportClass.return_group_id(body)
ssh = self._get_ssh(cred_id)
if ssh.shared and g.user_params['role'] != 1 and int(group_id) != int(ssh.group_id):
return roxywi_common.handler_exceptions_for_json_data(RoxywiPermissionError(), 'You cannot change shared parameters')
try:
self._check_is_correct_group(cred_id)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, ''), 404
return roxywi_common.handler_exceptions_for_json_data(e, '')
try:
ssh_mod.update_ssh_key(cred_id, body.name, body.password, body.key_enabled, body.username, group_id)
ssh_mod.update_ssh_key(body, group_id, cred_id)
return BaseResponse().model_dump(mode='json'), 201
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot update SSH key')
@ -192,7 +205,7 @@ class CredView(MethodView):
try:
self._check_is_correct_group(cred_id)
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, ''), 404
return roxywi_common.handler_exceptions_for_json_data(e, '')
try:
ssh_mod.delete_ssh_key(cred_id)
@ -245,16 +258,19 @@ class CredView(MethodView):
except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(e, 'Cannot upload SSH key')
@staticmethod
def _check_is_correct_group(cred_id: int):
def _check_is_correct_group(self, cred_id: int):
if g.user_params['role'] == 1:
return True
ssh = self._get_ssh(cred_id)
if int(ssh.group_id) != int(g.user_params['group_id']):
raise RoxywiGroupMismatch
@staticmethod
def _get_ssh(cred_id: int) -> Cred:
try:
ssh = cred_sql.get_ssh(cred_id)
return cred_sql.get_ssh(cred_id)
except RoxywiResourceNotFound:
raise RoxywiResourceNotFound
if ssh.group_id != g.user_params['group_id']:
raise RoxywiGroupMismatch
class CredsView(MethodView):
@ -306,6 +322,9 @@ class CredsView(MethodView):
private_key:
type: 'string'
description: 'SSH private key in base64 encoded format'
shared:
type: 'integer'
description: 'Is shared credential for other groups or not'
"""
group_id = SupportClass.return_group_id(query)
try: