This repository has been archived on 2023-11-03. You can view files and clone it, but cannot push or open issues or pull requests.
ACMS/src/server.py

358 lines
11 KiB
Python

#!/usr/bin/env python3.5
# -*- coding: utf-8 -*-
"""
A simple server for ACMS. Description to be improved.
"""
import os
import re
import ssl
import socket
import atexit
import threading
from colorama import init, Fore
from Server.UserList import UserList
from Server.ACL import AccessControlList
from Server.CommandException import CommandException
from Common.socketCommands import sendData, recvData
from Server.errorHandler import serverSendErrHandler, serverRecvErrHandler
from Server.miscCommands import helpController, historyController, addToHistory
from Server.prototypeVNC import startxController
from Server.fileCommands import DATA_PATH, rmController, mvController, mkdirController, cdController, lsController, cpController, vimController, findController, rightsController, chmodController, chownController
from Server.ulCommands import adduserController, removeuserController, addgroupController, delgroupController, addusergroupController, removeusergroupController, passwdController, groupsController
__authors__ = "HorlogeSkynet, Tatiyk, CaumartinYann"
__copyright__ = "Copyright 2017, ACMS"
__license__ = "GPLv3"
__status__ = "Production"
__date__ = "02/22/2017"
MAX_SESSIONS_PER_USER = 2
MAX_NUMBER_CLIENTS_PER_IP = 2
################
# Client class #
################
class ClientThread(threading.Thread):
def __init__(self, sock, address, port, mutex):
super(ClientThread, self).__init__()
self.sock = sock
self.address = address
self.port = port
self.mutex = mutex
self.HOME_DIR = DATA_PATH
self.currentWorkingDir = ''
self.username = ''
def run(self):
clients.append(self)
print("\nA new client thread has just started for \"" + self.address + ':' + str(self.port) + "\".")
displayStatus()
# Log in procedure #
while True:
# ATM, the client should sends the login followed by the password !
login = recvData(self.sock, serverRecvErrHandler, self.sock)
if not login:
self.stop()
return
if not login.startswith('login:'):
sendData(self.sock, 'LOGIN_ERROR', serverSendErrHandler, self.sock)
continue
login = login.split(':', 1)
if len(login) != 2:
sendData(self.sock, 'LOGIN_ERROR', serverSendErrHandler, self.sock)
continue
# Sends and ACK for the login reception
sendData(self.sock, 'LOGIN_OK', serverSendErrHandler, self.sock)
# Let's wait for the password reception
password = recvData(self.sock, serverRecvErrHandler, self.sock)
if not password:
self.stop()
return
if not password.startswith('password:'):
sendData(self.sock, 'LOGIN_ERROR', serverSendErrHandler, self.sock)
continue
password = password.split(':', 1)
if len(password) != 2:
sendData(self.sock, 'PASSWORD_ERROR', serverSendErrHandler, self.sock)
continue
# We have just received both username and password, let's check them and send an answer !
if userList.checkUserPassword(login[1], password[1]):
sendData(self.sock, 'PASSWORD_OK', serverSendErrHandler, self.sock)
break
else:
sendData(self.sock, "AUTHENTICATION_ERROR", serverSendErrHandler, self.sock)
continue
self.username = login[1]
# Contain session number here
if [client.username for client in clients].count(self.username) > MAX_SESSIONS_PER_USER:
sendData(self.sock, 'ERROR: Too many sessions are already opened for this account.', serverSendErrHandler, self.sock)
else:
self.checkAndSetHomeDir()
sendData(self.sock, 'NEW_PATH:' + '/' + self.currentWorkingDir.partition(DATA_PATH)[2], serverSendErrHandler, self.sock)
# Usage procedure #
while True:
command = recvData(self.sock, serverRecvErrHandler, self.sock)
if not command or not computeCommand(command, self):
break
addToHistory(self.username, command, acl)
self.stop()
print("\nThe thread for \"" + self.address + ':' + str(self.port) + "\" has just terminated.")
displayStatus()
def stop(self):
self.sock.close()
clients.remove(self)
def checkAndSetHomeDir(self):
tmp = self.HOME_DIR
self.HOME_DIR += self.username + '/'
if not os.path.exists(self.HOME_DIR):
try:
os.mkdir(self.HOME_DIR)
acl.addFile(self.username, self.HOME_DIR)
except:
print(Fore.RED + "Cannot create home directory for " + self.username + Fore.RESET)
self.currentWorkingDir = tmp
return
if acl.isAllowedToOn(self.username, 'execute', self.HOME_DIR):
self.currentWorkingDir = self.HOME_DIR
else:
self.currentWorkingDir = tmp
#####################
# Display functions #
#####################
def displayStatus():
print("There is now " + str(len(clients)) + " client" + ('s' if len(clients) > 1 else '') + " connected.\n")
def clearScreen():
os.system('cls' if os.name == 'nt' else 'clear')
#####################
# atexit() function #
#####################
def cleaningFunction():
for client in clients:
client.stop()
listenSock.close()
######################
# Commands functions #
######################
def computeCommand(command, client):
# Let's split this command around ' ' and '\t'
command = re.split('\s+', command)
try:
# If there are any spaces before the first word...
if not command[0].strip():
command.remove(command[0])
# Lower the first argument... Handy for one-word commands
command[0] = str.lower(command[0])
###########################
# Miscellaneous functions #
###########################
if command[0] == 'exit' and len(command) == 1:
return False
elif command[0] == 'clear' and len(command) == 1:
sendData(client.sock, 'CLEAR_SCREEN', serverSendErrHandler, client.sock)
elif (command[0] == 'help' or command[0] == '?') and len(command) == 1:
sendData(client.sock, helpController(acl.isAdministrator(client.username)), serverSendErrHandler, client.sock)
elif command[0] == 'history' and len(command) == 1:
sendData(client.sock, historyController(client.username), serverSendErrHandler, client.sock)
####################
# Graphic function #
####################
elif command[0] == 'startx' and len(command) == 1:
startxController(client.sock, clients.index(client))
##################
# File functions #
##################
elif command[0] == 'rm':
rmController(command, client, acl)
sendData(client.sock, "Done.", serverSendErrHandler, client.sock)
elif command[0] == 'mv':
mvController(command, client, acl)
sendData(client.sock, "Done.", serverSendErrHandler, client.sock)
elif command[0] == 'mkdir':
mkdirController(command, client, acl)
sendData(client.sock, "The directory has been created.", serverSendErrHandler, client.sock)
elif command[0] == 'cd':
cdController(command, client, acl)
sendData(client.sock, "NEW_PATH:" + '/' + client.currentWorkingDir.partition(DATA_PATH)[2], serverSendErrHandler, client.sock)
elif command[0] == 'ls':
sendData(client.sock, lsController(command, client, acl), serverSendErrHandler, client.sock)
elif command[0] == 'cp':
cpController(command, client, acl)
sendData(client.sock, "File or directory successfully copied !", serverSendErrHandler, client.sock)
elif command[0] == 'vim':
vimController(command, client, acl)
elif command[0] == 'view':
command[0] = 'vim'
vimController(command, client, acl, readOnly=True)
elif command[0] == 'find':
sendData(client.sock, findController(command, client, acl), serverSendErrHandler, client.sock)
elif command[0] == 'rights':
sendData(client.sock, rightsController(command, client, acl), serverSendErrHandler, client.sock)
elif command[0] == 'chmod':
sendData(client.sock, chmodController(command, client, acl), serverSendErrHandler, client.sock)
elif command[0] == 'chown':
sendData(client.sock, chownController(command, client, acl), serverSendErrHandler, client.sock)
#######################
# User List functions #
#######################
elif command[0] == 'adduser':
sendData(client.sock, adduserController(command, client, userList, acl), serverSendErrHandler, client.sock)
elif command[0] == 'removeuser':
sendData(client.sock, removeuserController(command, client, userList, acl), serverSendErrHandler, client.sock)
elif command[0] == 'addgroup':
sendData(client.sock, addgroupController(command, client, acl), serverSendErrHandler, client.sock)
elif command[0] == 'delgroup':
sendData(client.sock, delgroupController(command, client, acl), serverSendErrHandler, client.sock)
elif command[0] == 'addusergroup':
sendData(client.sock, addusergroupController(command, client, acl), serverSendErrHandler, client.sock)
elif command[0] == 'removeusergroup':
sendData(client.sock, removeusergroupController(command, client, acl), serverSendErrHandler, client.sock)
elif command[0] == 'passwd':
sendData(client.sock, passwdController(command, client, userList, acl), serverSendErrHandler, client.sock)
elif command[0] == 'groups' and len(command) == 1:
sendData(client.sock, groupsController(client.username, userList, acl), serverSendErrHandler, client.sock)
# Unknown command
else:
sendData(client.sock, "Unknown command (or is there too many arguments ?).\nYou should try `help` or your \'TAB\' key.", serverSendErrHandler, client.sock)
except CommandException as e:
sendData(client.sock, e.message, serverSendErrHandler, client.sock)
return True
##########
# main() #
##########
if __name__ == '__main__':
# Socket settings
TCP_IP = "0.0.0.0"
TCP_PORT = 4242
# Displays some stuff
clearScreen()
print("\n\tWelcome on the ACMS\'s Server !")
# An instance for our user list
userList = UserList()
# An instance for our ACL
acl = AccessControlList()
# Server socket initialization
listenSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listenSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
listenSock.bind((TCP_IP, TCP_PORT))
listenSock.listen()
print("Listening for clients connection on \"" + str(TCP_IP) + ':' + str(TCP_PORT) + "\"...\n")
# Client threads container
clients = []
# The clients' list exists now, let's add a special cleaning function
atexit.register(cleaningFunction)
# Our mutex for the clients
mutex = threading.RLock()
# Colorama module initialization
init(autoreset=True)
while True:
# We wait and accept each entering connection
sock, (address, port) = listenSock.accept()
# Limit DDoS here
if [client.address for client in clients].count(address) + 1 > MAX_NUMBER_CLIENTS_PER_IP:
sock.close()
continue
# Creates a SSL socket from the one that has just been set up above
sock = ssl.wrap_socket(sock, server_side=True, certfile="Server/Credentials/server.crt", keyfile="Server/Credentials/server.key", ssl_version=ssl.PROTOCOL_TLSv1_2)
# Let's run a new thread for the connection with this client
ClientThread(sock, address, port, mutex).start()