245 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python3
 | |
| # -*- coding: utf-8 -*-
 | |
| 
 | |
| """
 | |
| A simple class to manage an ACL.
 | |
| """
 | |
| 
 | |
| 
 | |
| import os
 | |
| import json
 | |
| 
 | |
| from colorama import Fore
 | |
| from .fileCommands import DATA_PATH
 | |
| from .CommandException import CommandException
 | |
| 
 | |
| 
 | |
| __author__ = "HorlogeSkynet"
 | |
| __copyright__ = "Copyright 2017, ACMS"
 | |
| __license__ = "GPLv3"
 | |
| __status__ = "Production"
 | |
| __date__ = "02/23/2017"
 | |
| 
 | |
| 
 | |
| class AccessControlList():
 | |
| 
 | |
| 	"""
 | |
| 	The only attribute (useful) in this class is the dictionary, and it is declared as a "private attribute".
 | |
| 
 | |
| 	The dictionary is constructed as a JSON file as below (a piece of advice: use a JSON editor):
 | |
| 	{"groups": {"dentistes": ["Marie", "Xavier"], "chirurgien": ["Jules"], "pédiatres": ["Jules"]}, "files": {"filename_2": {"owner": "Marie", "write": ["dentistes"], "read": ["Xavier"], "execute": ["pédiatres"]}, "filename_1": {"owner": "Marie", "write": ["pédiatres", "dentistes"], "read": ["Xavier"], "execute": ["chirurgien"]}}}
 | |
| 
 | |
| 	During the class initialization, the dictionary is filled with data present in the backup file.
 | |
| 	Each authorized and validated action implies the dumping of the new dictionary in the backup file (persistent data).
 | |
| 	"""
 | |
| 
 | |
| 	def __init__(self):
 | |
| 		self.__FILE = DATA_PATH + 'passwd.json'
 | |
| 		self.__data = {"groups": {}, "files": {}}
 | |
| 
 | |
| 		self.loadFromFile()
 | |
| 		self.checkIntegrity()
 | |
| 
 | |
| 	def loadFromFile(self):
 | |
| 		if os.path.exists(self.__FILE):
 | |
| 			with open(self.__FILE, 'r') as file:
 | |
| 				try:
 | |
| 					self.__data = json.load(file)
 | |
| 
 | |
| 				except json.JSONDecodeError as e:
 | |
| 					print(Fore.RED + "The backup file (\"" + self.__FILE + "\") looks corrupted (" + e.msg + ' [' + str(e.lineno) + '; ' + str(e.colno) + ']' + "). Contact an administrator." + Fore.RESET)
 | |
| 
 | |
| 	def saveToFile(self):
 | |
| 			with open(self.__FILE, 'w+') as file:
 | |
| 				try:
 | |
| 					json.dump(self.__data, file, ensure_ascii=False, indent='\t')
 | |
| 
 | |
| 				except (TypeError, json.JSONEncodeError):
 | |
| 					print(Fore.RED + "An invalid object is present in the ACL dictionary, we\'d advise you to fix the backup in the \"" + self.__FILE + "\" file, and import it." + Fore.RESET)
 | |
| 
 | |
| 	def checkIntegrity(self):
 | |
| 		results = []
 | |
| 
 | |
| 		def browseFiles(path):
 | |
| 			for file in os.listdir(path):
 | |
| 				temp = path + file
 | |
| 				if os.path.isdir(path + file):
 | |
| 					results.append(temp + '/')
 | |
| 					browseFiles(temp + '/')
 | |
| 
 | |
| 				else:
 | |
| 					results.append(temp)
 | |
| 
 | |
| 		# Browses each file in the database and stores them into a list
 | |
| 		browseFiles(DATA_PATH)
 | |
| 
 | |
| 		# Computes the symmetrical differences between the files browsed and the ones present in the ACL
 | |
| 		differences = set(results).symmetric_difference(list(self.__data['files'].keys()))
 | |
| 		if len(differences) != 0:
 | |
| 			for difference in differences:
 | |
| 				print(Fore.RED + "\nThe element \"" + difference + "\" is not present in the ACL or into the database. Please fix this difference.\n" + Fore.RESET)
 | |
| 
 | |
| 			quit()
 | |
| 
 | |
| 		else:
 | |
| 			print(Fore.GREEN + "\nYour database is clean ! Let\'s proceed.\n" + Fore.RESET)
 | |
| 
 | |
| 	def isAllowedToOn(self, user, action, file):
 | |
| 		try:
 | |
| 			# This 'user' is allowed to perform this 'action' on this 'file' if: He's the owner, he has access, its group had access
 | |
| 			if user == self.__data['files'][file]['owner'] or user in self.__data['files'][file][action] or [group in self.__data['files'][file][action] for group in self.getGroupsByUser(user)].count(True) > 0:
 | |
| 				return True
 | |
| 
 | |
| 			else:
 | |
| 				return False
 | |
| 
 | |
| 		except:
 | |
| 			return None
 | |
| 
 | |
| 	def createGroup(self, group):
 | |
| 		if list(self.__data['groups'].keys()).count(group) == 0:
 | |
| 			self.__data['groups'][group] = []
 | |
| 			self.saveToFile()
 | |
| 
 | |
| 		else:
 | |
| 			raise CommandException("This group already exists !")
 | |
| 
 | |
| 	def removeGroup(self, group):
 | |
| 		if list(self.__data['groups'].keys()).count(group) != 0:
 | |
| 			if group != "administrator":
 | |
| 				if len(self.__data['groups'][group]) == 0:
 | |
| 					del self.__data['groups'][group]
 | |
| 					self.saveToFile()
 | |
| 
 | |
| 				else:
 | |
| 					raise CommandException("This group is not empty, you can\'t remove it.")
 | |
| 
 | |
| 			else:
 | |
| 				raise CommandException("You can\'t remove this special group.")
 | |
| 
 | |
| 		else:
 | |
| 			raise CommandException("This group does not exist...")
 | |
| 
 | |
| 	def addUserInGroup(self, user, group):
 | |
| 		try:
 | |
| 			if list(self.__data['groups'].keys()).count(group) != 0:
 | |
| 				if self.__data['groups'][group].count(user) != 0:
 | |
| 					raise CommandException("This user is already in this group !")
 | |
| 
 | |
| 				else:
 | |
| 					self.__data['groups'][group].append(user)
 | |
| 
 | |
| 			else:
 | |
| 				self.__data['groups'][group] = [user]
 | |
| 
 | |
| 			self.saveToFile()
 | |
| 
 | |
| 		except:
 | |
| 			raise CommandException("Can\'t add this user into this group.")
 | |
| 
 | |
| 	def removeUserFromGroup(self, user, group):
 | |
| 		try:
 | |
| 			self.__data['groups'][group].remove(user)
 | |
| 			self.saveToFile()
 | |
| 
 | |
| 		except:
 | |
| 			raise CommandException("Can\'t remove this user from this group.")
 | |
| 
 | |
| 	def addFile(self, user, file):
 | |
| 		if list(self.__data['files'].keys()).count(file) == 0:
 | |
| 			read = []
 | |
| 			execute = []
 | |
| 			write = []
 | |
| 
 | |
| 			# Only owner and its group (plus selected persons) can access this file
 | |
| 			read.extend(self.getGroupsByUser(user))
 | |
| 			# Only owner (plus selected persons) can modify this file
 | |
| 			# write.append(user) (Not mandatory, the own test is done)
 | |
| 			# Only owner and its group (plus selected persons) can execute this file
 | |
| 			execute.extend(self.getGroupsByUser(user))
 | |
| 
 | |
| 			self.__data['files'][file] = {'owner': user, 'read': read, 'write': write, 'execute': execute}
 | |
| 
 | |
| 			self.saveToFile()
 | |
| 
 | |
| 		else:
 | |
| 			raise CommandException("This file already exist !")
 | |
| 
 | |
| 	def deleteFile(self, user, file):
 | |
| 		try:
 | |
| 			del self.__data['files'][file]
 | |
| 			self.saveToFile()
 | |
| 
 | |
| 		except:
 | |
| 			raise CommandException("This file does not exist...")
 | |
| 
 | |
| 	def renameFile(self, user, file, newFileName):
 | |
| 		try:
 | |
| 			if list(self.__data['files'].keys()).count(newFileName) == 0:
 | |
| 				self.__data['files'][newFileName] = self.__data['files'][file]
 | |
| 				del self.__data['files'][file]
 | |
| 				self.saveToFile()
 | |
| 
 | |
| 			else:
 | |
| 				raise CommandException("A file with the new name specified already exist !")
 | |
| 
 | |
| 		except:
 | |
| 			raise CommandException("An error occurred...")
 | |
| 
 | |
| 	def setRightsOnFile(self, user, file, read, write, execute):
 | |
| 		try:
 | |
| 			if user == self.__data['files'][file]['owner'] or self.isAdministrator(user):
 | |
| 				self.__data['files'][file] = {'owner': self.__data['files'][file]['owner'], 'read': read, 'write': write, 'execute': execute}
 | |
| 				self.saveToFile()
 | |
| 
 | |
| 			else:
 | |
| 				raise CommandException("You\'re not allowed to perform this operation !")
 | |
| 
 | |
| 		except:
 | |
| 			raise CommandException("This file does not exist...")
 | |
| 
 | |
| 	def getOwnerOnFile(self, file):
 | |
| 		if list(self.__data['files'].keys()).count(file):
 | |
| 			return str(self.__data['files'][file]['owner'])
 | |
| 		else:
 | |
| 			raise CommandException("This file does not exist...")
 | |
| 
 | |
| 	def getRightsOnFile(self, file):
 | |
| 		if list(self.__data['files'].keys()).count(file):
 | |
| 			return self.__data['files'][file]['read'], self.__data['files'][file]['write'], self.__data['files'][file]['execute']
 | |
| 		else:
 | |
| 			raise CommandException("This file does not exist...")
 | |
| 
 | |
| 	def changeOwnOnFile(self, user, file, newUser):
 | |
| 		try:
 | |
| 			if user == self.__data['files'][file]['owner'] or self.isAdministrator(user):
 | |
| 				self.__data['files'][file]['owner'] = newUser
 | |
| 				self.saveToFile()
 | |
| 
 | |
| 			else:
 | |
| 				raise CommandException("You\'re not allowed to perform this operation !")
 | |
| 
 | |
| 		except:
 | |
| 			raise CommandException("This file does not exist...")
 | |
| 
 | |
| 	def isAdministrator(self, user):
 | |
| 		if user in self.__data['groups']['administrator']:
 | |
| 			return True
 | |
| 		else:
 | |
| 			return False
 | |
| 
 | |
| 	# A simple function in order to gather the groups where an user is present
 | |
| 	def getGroupsByUser(self, user):
 | |
| 		userGroups = []
 | |
| 
 | |
| 		try:
 | |
| 			for group, members in self.__data['groups'].items():
 | |
| 				if user in members:
 | |
| 					userGroups.append(group)
 | |
| 
 | |
| 		except:
 | |
| 			pass
 | |
| 
 | |
| 		finally:
 | |
| 			return userGroups
 | 
