Skip to content

Commit

Permalink
Merge pull request #23 from abcdesktopio/dev
Browse files Browse the repository at this point in the history
add new entries in /API/manager add overwrite_environment_variable_for_application.sh
  • Loading branch information
alexandredevely authored May 7, 2024
2 parents 7f2cd4b + 115f710 commit 2f748e8
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 70 deletions.
90 changes: 64 additions & 26 deletions controllers/manager_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
import logging
import cherrypy

import distutils.util
from typing_extensions import assert_type

from oc.od.base_controller import BaseController
import oc.od.composer
import oc.od.services
import oc.cherrypy
import oc.logging
import distutils.util

from oc.od.services import services


Expand All @@ -37,6 +40,15 @@ class ManagerController(BaseController):
def __init__(self, config_controller=None):
super().__init__(config_controller)

@cherrypy.expose
@cherrypy.tools.json_out()
def healtz(self):
# check if request is allowed, raise an exception if deny
self.is_permit_request()
cherrypy.response.notrace = True
return { 'controler': self.__class__.__name__, 'status': 'ok' }


# buildapplist request is protected by is_permit_request()
@cherrypy.expose
@cherrypy.tools.json_out()
Expand Down Expand Up @@ -250,8 +262,8 @@ def ban( self, collection, *args ):
self.is_permit_request()
if cherrypy.request.method == 'GET':
return self.handle_ban_GET( collection, args )
elif cherrypy.request.method == 'PUT':
return self.handle_ban_PUT( collection, args )
elif cherrypy.request.method == 'POST':
return self.handle_ban_POST( collection, args )
elif cherrypy.request.method == 'DELETE':
return self.handle_ban_DELETE( collection, args )

Expand All @@ -277,18 +289,34 @@ def handle_desktop_GET( self, args ):
describedesktop = oc.od.composer.describe_desktop_byname(desktop_name)
return describedesktop

# use a specify desktop
if len(args)==2 and args[1]=="container":
# list container for a desktop
# /API/manager/desktop/hermes-8a49ca1a-fcc6-4b7b-960f-5a27debd4773/container
container = oc.od.composer.list_container_byname(desktop_name)
return container

container_id = args[2]
if not isinstance( container_id, str):
raise cherrypy.HTTPError(status=400, message='Invalid parameters Bad Request')

# use a specify desktop
if len(args) > 1:

if args[1]=="resources_usage":
# specify desktop
if len(args)==2 :
# list container for a desktop
# /API/manager/desktop/hermes-8a49ca1a-fcc6-4b7b-960f-5a27debd4773/mem
resource = oc.od.composer.get_desktop_resources_usage(desktop_name)
return resource

if args[1]=="container":
# specify desktop
if len(args)==2 :
# list container for a desktop
# /API/manager/desktop/hermes-8a49ca1a-fcc6-4b7b-960f-5a27debd4773/container
container = oc.od.composer.list_container_byname(desktop_name)
return container

if len(args)==3:
container_id = args[2]
if not isinstance( container_id, str):
raise cherrypy.HTTPError(status=400, message='Invalid parameters Bad Request')
# list container for a desktop
# /API/manager/desktop/hermes-8a49ca1a-fcc6-4b7b-960f-5a27debd4773/container/
container = oc.od.composer.describe_container( desktop_name, container=container_id )
return container

# specify desktop and specify container
if len(args)==3 and args[1]=="container":
# list container for a desktop
# /API/manager/desktop/hermes-8a49ca1a-fcc6-4b7b-960f-5a27debd4773/container/
Expand Down Expand Up @@ -325,38 +353,48 @@ def handle_desktop_DELETE( self, args ):



def handle_ban_GET( self, collection, args ):
def handle_ban_GET( self, collection:str, args:tuple ):
self.logger.debug('')
assert_type( collection, str )

# handle GET request to ban
if not services.fail2ban.iscollection( collection ):
raise cherrypy.HTTPError(status=400, message='Invalid type parameters Bad Request')
if not isinstance( args, tuple):
raise cherrypy.HTTPError(status=400, message='Invalid type parameters Bad Request')
if len(args)!=0:
raise cherrypy.HTTPError(status=400, message='Invalid type parameters Bad Request')
# /API/ban/ipaddr
# /API/ban/login
# list all desktops
listban = services.fail2ban.listban( collection_name=collection )
return listban
if len(args)==0:
# /API/ban/ipaddr : list all ban
# /API/ban/login : list all ban
listban = services.fail2ban.listban( collection_name=collection )
return listban
elif len(args)==1:
name = args[0]
ban_result = services.fail2ban.find_ban( name, collection_name=collection)
return ban_result
else:
raise cherrypy.HTTPError(status=400, message='Invalid type parameters Bad Request')


def handle_ban_PUT( self, collection, args ):
def handle_ban_POST( self, collection:str, args:tuple ):
self.logger.debug('')
# handle GET request to ban
# handle POST request to ban
if not services.fail2ban.iscollection( collection ):
raise cherrypy.HTTPError(status=400, message='Invalid type parameters Bad Request')
if not isinstance( args, tuple):
raise cherrypy.HTTPError(status=400, message='Invalid type parameters Bad Request')
if len(args)!=1:
raise cherrypy.HTTPError(status=400, message='Invalid type parameters Bad Request')
ban = services.fail2ban.ban( args[0], collection_name=collection)
if ban is None:
raise cherrypy.HTTPError(status=500, message='Invalid type parameters bad request')
if isinstance( ban, str ):
raise cherrypy.HTTPError(status=400, message=ban)
return ban


def handle_ban_DELETE( self, collection, args ):
self.logger.debug('')
# handle GET request to ban
# handle DELETE request to ban
if not services.fail2ban.iscollection( collection ):
raise cherrypy.HTTPError(status=400, message='Invalid type parameters Bad Request')
if not isinstance( args, tuple):
Expand Down
17 changes: 10 additions & 7 deletions oc/od/base_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,43 +196,46 @@ def is_apikey(self):
if bReturn is True:
break
return bReturn

def raise_http_error_message( self, error_message:str, status=403 ):
self.logger.error( error_message )
raise cherrypy.HTTPError( status, error_message)

def is_permit_request(self):

if not self.enable :
raise cherrypy.HTTPError( 400, "The controller is disabled in configuration file")
self.raise_http_error_message( '403.10 - Invalid configuration' )

# Check if the controller has an apikey filter
# if set it must match to the apikey list entries
if not self.apifilter():
# 403 - rejected.
raise cherrypy.HTTPError(status=403, message='X-API-Key http header is denied')
self.raise_http_error_message( '403.1 - Execute access forbidden' )

# Check if the controller has an ip filter
# if set it must match to the ip network entries
if not self.ipfilter():
# 403.6 - IP address rejected.
raise cherrypy.HTTPError(status=403, message='ip address is denied')
self.raise_http_error_message( '403.6 - IP address rejected' )

if isinstance( self.requestsallowed, dict ):
# read the request path
path = cherrypy.request.path_info
arg = path.split('/')
if len( arg ) < 3 :
# the min value is 3
self.logger.error( 'request is denied' )
raise cherrypy.HTTPError(400, 'Request is denied by configuration file')
self.raise_http_error_message( '403.12 - Mapper denied access. Invalid request' )

# read example
# 'getdesktopdescription' from str '/composer/getdesktopdescription'
request_info = arg[2]

# check if method is allowed in config file
is_allowed = self.requestsallowed.get( request_info )

# if is_allowed is None, do not raise Error
if is_allowed is False :
self.logger.error( 'request is denied' )
raise cherrypy.HTTPError(400, 'Request is denied by configuration file')
self.raise_http_error_message( '403.8 - Site access denied' )

def apifilter(self):
self.logger.debug('')
Expand Down
14 changes: 10 additions & 4 deletions oc/od/composer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
import logging
from typing_extensions import assert_type
import requests

from oc.cherrypy import getclientipaddr
from oc.od.desktop import ODDesktop

import oc.od.settings as settings # Desktop settings lib
import oc.pyutils
import oc.logging
Expand Down Expand Up @@ -85,7 +85,7 @@ def securitypoliciesmatchlabelvalue( desktop:ODDesktop, authinfo:AuthInfo, label
return result


def opendesktop(authinfo, userinfo, args ):
def opendesktop(authinfo:AuthInfo, userinfo:AuthUser, args ):
"""open a new or return a desktop
Args:
authinfo (AuthInfo): authentification data
Expand Down Expand Up @@ -198,7 +198,7 @@ def runwebhook( c, messageinfo=None ):
def remove_desktop_byname( desktop_name:str ):
myOrchestrator = selectOrchestrator()
(authinfo, userinfo) = myOrchestrator.find_userinfo_authinfo_by_desktop_name( name=desktop_name )
return myOrchestrator.removedesktop( authinfo, userinfo )
return removedesktop( authinfo, userinfo )

def stop_container_byname( desktop_name:str, container ):
myOrchestrator = selectOrchestrator()
Expand All @@ -225,6 +225,11 @@ def remove_container_byname(desktop_name: str, container_id:str):
(authinfo, userinfo) = myOrchestrator.find_userinfo_authinfo_by_desktop_name( name=desktop_name )
return myOrchestrator.removeContainerApp(authinfo,userinfo,container_id=container_id)

def get_desktop_resources_usage(desktop_name:str):
myOrchestrator = selectOrchestrator()
(authinfo, userinfo) = myOrchestrator.find_userinfo_authinfo_by_desktop_name( name=desktop_name )
return myOrchestrator.getdesktop_resources_usage(authinfo,userinfo )


def fakednsquery( userid ):
logger.debug( locals() )
Expand Down Expand Up @@ -320,7 +325,8 @@ def removedesktop( authinfo:AuthInfo, userinfo:AuthUser ):
"""
myOrchestrator = selectOrchestrator()
# remove the desktop
removed_desktop = myOrchestrator.removedesktop( authinfo, userinfo )
myDesktop = myOrchestrator.removedesktop( authinfo, userinfo )
removed_desktop = isinstance( myDesktop, ODDesktop)
return removed_desktop


Expand Down
12 changes: 7 additions & 5 deletions oc/od/desktop.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,10 @@
logger = logging.getLogger(__name__)






@oc.logging.with_logger()
class ODDesktop(object):

def __init__(self, nodehostname=None, hostname=None, name=None, desktop_id=None, ipAddr=None, status=None, container_id=None, container_name=None, vncPassword=None, fqdn=None, desktop_interfaces=None, websocketroute=None, websocketrouting=None, xauthkey=None, pulseaudio_cookie=None, broadcast_cookie=None, storage_container_id=None, labels=None, websockettcpport=None, uid=None ):
def __init__(self, nodehostname=None, hostname=None, name=None, desktop_id=None, ipAddr=None, status=None, container_id=None, container_name=None, vncPassword=None, fqdn=None, desktop_interfaces=None, websocketroute=None, websocketrouting=None, xauthkey=None, pulseaudio_cookie=None, broadcast_cookie=None, storage_container_id=None, labels=None, websockettcpport=None, uid=None, creation_timestamp=None ):
self._id = desktop_id
self._ipAddr = ipAddr
self._status = status
Expand Down Expand Up @@ -53,6 +49,7 @@ def __init__(self, nodehostname=None, hostname=None, name=None, desktop_id=None,
self._storage_container_id = storage_container_id
self._labels = labels
self._uid = uid
self._creation_timestamp = creation_timestamp

# id is the container id in docker mode
# id is the pod id in kubernetes node
Expand Down Expand Up @@ -95,6 +92,10 @@ def nodehostname(self):
@property
def hostname(self):
return self._hostname

@property
def creation_timestamp(self):
return self._creation_timestamp

@property
def fqdn(self):
Expand Down Expand Up @@ -208,6 +209,7 @@ def to_dict(self):
'ipAddr': self._ipAddr,
'status': self._status,
'container_id' : self._container_id,
'creation_timestamp': self._creation_timestamp,
'nodehostname' : self._nodehostname,
'vncPassword' : self._vncPassword,
'hostname' : self._hostname,
Expand Down
36 changes: 30 additions & 6 deletions oc/od/fail2ban.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,33 @@ def fail_ip( self, value ):

return self.fail( value, collection_name = self.ip_collection_name )

def fail_login( self, value ):
def fail_login( self, value:str ):
# if ban is not enable nothing to do
if not self.enable:
return
return self.fail( value, collection_name = self.login_collection_name )

def find_ban( self, value:str, collection_name:str )->dict:
myban = { 'banexpireAfterSeconds': self.banexpireAfterSeconds }
collection = self.get_collection( collection_name )
bfind = collection.find_one({ self.index_name: value})
if isinstance(bfind, dict):
myban['id'] = bfind.get('id')
myban['date'] = None
mydate = bfind.get('date')
if isinstance(mydate, datetime.datetime ):
myban['date'] = mydate.isoformat()
myban[self.counter] = bfind.get(self.counter,0)
myban['isban'] = myban[self.counter] >= self.failmaxvaluebeforeban

else:
myban['id'] = value
myban['date'] = None
myban[self.counter] = 0
myban['isban'] = False
return myban


def isban( self, value, collection_name ):
"""isban
Expand Down Expand Up @@ -154,11 +175,14 @@ def updateorinsert( self, collection, bUpdate, value, counter ):
def ban( self, value, collection_name ):
myban = None
if not self.sanity( value, self.sanity_filter.get(collection_name)):
self.logger.error("bad parameter sanity check")
return False
error_message = f"bad value sanity check {value} for {collection_name}"
self.logger.error(error_message)
return error_message
collection = self.get_collection( collection_name )
bfind = collection.find_one({ self.index_name: value})
myban = self.updateorinsert( collection=collection, bUpdate=bfind, value=value, counter=self.failmaxvaluebeforeban )
if isinstance( myban, pymongo.results.UpdateResult ):
myban = myban.raw_result
return myban

def drop( self, collection_name ):
Expand All @@ -167,14 +191,14 @@ def drop( self, collection_name ):
self.init_collection(collection_name=collection_name)

def unban( self, value, collection_name ):
myban = False
myban = {'n': 0, 'ok': 0}
if not self.sanity( value, self.sanity_filter.get(collection_name)):
self.logger.error("bad parameter sanity check")
return False
return myban
collection = self.get_collection( collection_name )
delete_one = collection.delete_one({ self.index_name: value})
if isinstance( delete_one, pymongo.results.DeleteResult ):
myban = True
myban = delete_one.raw_result
return myban

def listban( self, collection_name ):
Expand Down
Loading

0 comments on commit 2f748e8

Please sign in to comment.