Mini Shell
Direktori : /usr/bin/ |
|
Current File : //usr/bin/lvemanager-service |
#!/opt/cloudlinux/venv/bin/python3 -bb
# coding=utf-8
#
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2021 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT
#
import base64
import json
import os
import uuid
from encodings.base64_codec import base64_encode
from pwd import getpwnam
import aiohttp_jinja2
import jinja2
import pam
from aiohttp import web
from aiohttp.web_request import Request
from aiohttp.web_response import Response
from aiohttp_session import setup as setup_session
from aiohttp_security import is_anonymous, remember, forget, \
setup as setup_security, SessionIdentityPolicy, authorized_userid, check_permission
from aiohttp_security.abc import AbstractAuthorizationPolicy
from aiohttp_session.cookie_storage import EncryptedCookieStorage
from cryptography import fernet
from constants import *
from utils import parse_params, run_cmd_pw, get_user_data, get_service_port,\
get_ssl_context, get_user_type, get_user_plugins
# ToDo(dpoleev): Use PKG_VERSION from lvemanager package after migrate to python 3.7
PKG_VERSION = open('/usr/share/l.v.e-manager/version').read().strip()
SERVER_PATH = os.path.dirname(os.path.realpath(__file__))
STATIC_FILE_PATH = os.path.join(SERVER_PATH, '../../commons/spa-resources/')
CLOUDLINUX_CLI = '/usr/share/l.v.e-manager/utils/cloudlinux-cli.py'
CLOUDLINUX_USER_CLI = '/usr/share/l.v.e-manager/utils/cloudlinux-cli-user.py'
DISABLE_CSP_FLAG='/var/lve/disable_csp.flag'
SECURITY_HEADERS = {
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"Content-Security-Policy": "default-src 'self' 'unsafe-inline' *.cloudlinux.com *.googleapis.com;object-src 'none';font-src *;script-src 'self' 'unsafe-eval' 'unsafe-inline' *.cloudlinux.com;img-src 'self' data:"
}
class AuthorizationPolicy(AbstractAuthorizationPolicy):
"""
get information about user by identity
"""
async def authorized_userid(self, identity):
pw = getpwnam(identity)
result = {
'pw': pw,
'type': get_user_type(pw.pw_name)
}
return result
async def permits(self, identity, permission, context=None):
return bool(identity)
async def handler_root(request):
"""
Main page
"""
is_logged = not await is_anonymous(request)
if is_logged:
user_data = await authorized_userid(request)
if user_data['type'] == TYPE_ADMIN:
return await admin_page(request)
elif user_data['type'] == TYPE_RESELLER:
return await reseller_page(request)
else:
return await user_page(request)
else:
return await login_page(request)
@aiohttp_jinja2.template('login.html')
async def login_page(request: Request):
"""
Login page
"""
return {
'login_error': request.query_string == 'incorrect'
}
@aiohttp_jinja2.template('user.html')
async def user_page(request):
"""
List applications page
"""
return {
'userdata': await authorized_userid(request),
'apps': get_user_plugins()
}
async def admin_page(request):
"""
Open admin LveManager
"""
response = await show_app('main', await authorized_userid(request), request)
set_csrf_token(response)
return response
async def reseller_page(request):
"""
Open Reseller LveManager
"""
response = await show_app('main', await authorized_userid(request), request)
set_csrf_token(response)
return response
async def handler_login(request: Request):
"""
Check authorization and show error message
"""
redirect_response = web.HTTPFound('/')
data = await request.post()
pam_object = pam.pam()
if pam_object.authenticate(data.get('username'), data.get('password'), service='system-auth'):
await remember(request, redirect_response, data.get('username'))
return redirect_response
else:
return web.HTTPFound('/?incorrect')
async def handler_app(request: Request):
"""
Show app handler
"""
plugin_name = request.match_info['plugin_name']
userdata = await authorized_userid(request)
response = await show_app(plugin_name, userdata, request)
set_csrf_token(response)
return response
@aiohttp_jinja2.template('app.html')
async def show_app(plugin_name, userdata, request: Request):
"""
Show certain SPA application
:param plugin_name: plugin name show what bundle and title should be used
:param userdata: information about user
:param request:Request for rendering jinja template
:return:
"""
plugin_title = PLUGINS.get(plugin_name).get('title')
panel_data = await get_user_data(userdata)
return {
'plugin_title': plugin_title,
'plugin_name': plugin_name,
'panel_data': panel_data,
'pluginVersion': PKG_VERSION
}
async def handler_logout(request):
"""
Logout handler: remove cookies information
"""
redirect_response = web.HTTPFound('/')
await forget(request, redirect_response)
return redirect_response
async def handler_request(request):
"""
Middleware for providing requests to cloudlinux-cli level
"""
check_csrf_token(request)
plugin_name = request.match_info['plugin_name']
plugin_title = PLUGINS.get(plugin_name).get('title')
await check_permission(request, plugin_name)
user_data = await authorized_userid(request)
request_data = parse_params(await request.post())
data = {
'plugin_name': plugin_name,
'owner': user_data['type'],
'command': request_data.get('command'),
'params': request_data.get('params') or {},
}
if 'mockJson' in request_data:
data['mockJson'] = request_data.get('mockJson')
if 'lang' in request_data:
data['lang'] = request_data.get('lang')
if 'method' in request_data:
data['method'] = request_data.get('method')
if user_data['pw'].pw_uid > 0:
data['user_info'] = {
'username': user_data['pw'].pw_name,
'lve-id': user_data['pw'].pw_uid,
}
cli_file_path = CLOUDLINUX_USER_CLI if user_data['type'] not in [TYPE_ADMIN, TYPE_RESELLER] else CLOUDLINUX_CLI
# Show unavailable page for end user if CLOUDLINUX_CLI missed
if not os.path.isfile(cli_file_path):
return sendError({
'code': 503,
'context': {'pluginName': plugin_title },
'error_id': 'ERROR.not_available_plugin',
'icon': 'disabled',
'result': ''}, 1)
cli_comand = [cli_file_path, '--data={}'.format(base64_encode(json.dumps(data)
.encode('utf8').strip())[0].decode('utf-8'))]
(retcode, stdout, stderr) = await run_cmd_pw(user_data['pw'], cli_comand)
# If decode_json is catched an exeption, send error header with backtrace
try:
json_data = json.loads(stdout)
except:
return sendError('ERROR.wrong_received_data', 0, 0, stdout + stderr)
if json_data.get('result') not in ['success', 'rollback']:
return sendError(json_data, 1)
if stdout == '':
return sendError('RESPONSE OF COMMAND IS EMPTY');
return web.Response(body=stdout + stderr, content_type='application/json')
def sendError(error_message, is_json = False, logout_signal = False, details = ''):
if is_json:
return web.json_response(error_message, status=503)
else:
response = json.dumps({
'result': error_message,
'logoutSignal': 1 if logout_signal else 0,
'details': details
})
return web.Response(status=503, body=response, content_type='application/json')
def make_app():
"""
Prepare web server
"""
app = web.Application()
# add the routes
app.add_routes([
web.get('/', handler_root),
web.post('/login', handler_login),
web.get('/app/{plugin_name}', handler_app),
web.get('/logout', handler_logout),
web.post('/send-request/{plugin_name}', handler_request),
web.static('/assets', STATIC_FILE_PATH)
])
# set up policies
policy = SessionIdentityPolicy()
setup_security(app, policy, AuthorizationPolicy())
# we need to initialize aiohttp_session
fernet_key = fernet.Fernet.generate_key()
secret_key = base64.urlsafe_b64decode(fernet_key)
setup_session(app, EncryptedCookieStorage(secret_key))
app.on_response_prepare.append(set_security_headers)
return app
def set_csrf_token(response: Response):
"""
Generate random csrf token and set to cookie
"""
response.set_cookie('csrftoken', str(uuid.uuid4()))
def check_csrf_token(request: Request):
"""
Check csrf token
"""
if not request.cookies.get('csrftoken') or request.cookies.get('csrftoken') != request.headers.get('X-CSRFToken'):
raise web.HTTPForbidden(body='BAD FORGERY PROTECTION TOKEN')
async def set_security_headers(request, response):
"""
Add security headers
"""
if os.path.isfile(DISABLE_CSP_FLAG):
return
for key, value in SECURITY_HEADERS.items():
response.headers[key] = value
if __name__ == '__main__':
app = make_app()
env = aiohttp_jinja2.setup(
app,
loader=jinja2.FileSystemLoader(
os.path.join(SERVER_PATH, 'templates')),
context_processors=[],
autoescape=True,
)
web.run_app(
app,
ssl_context=get_ssl_context(),
port=get_service_port()
)
Zerion Mini Shell 1.0