#!/usr/share/ucs-test/runner python
## desc: Test roleshares creation
## roles: [domaincontroller_master]
## tags: [apptest,ucsschool]
## exposure: dangerous
## packages:
##    - ucs-school-import
##    - univention-samba | univention-samba4
##    - ucs-school-roleshares

import sys
import subprocess
from ldap.filter import escape_filter_chars
from univention.testing.codes import TestCodes
import univention.testing.utils as utils
import univention.testing.ucr as testing_ucr
import univention.testing.ucsschool as testing_ucsschool
from univention.admin.uldap import explodeDn
from ucsschool.lib.schoolldap import LDAP_Connection, set_credentials, MACHINE_READ
from ucsschool.lib.models import School
import ucsschool.lib.roleshares as roleshares
from ucsschool.lib.roles import role_pupil
try:
	from univention.testing.ucs_samba import wait_for_drs_replication
except ImportError:
	def wait_for_drs_replication(ldap_filter, attrs=None):
		pass
from ucsschool.lib.models.utils import add_stream_logger_to_schoollib
add_stream_logger_to_schoollib()


# Initialize globals
ucr = testing_ucr.UCSTestConfigRegistry()
ucr.load()
TESTS_DOMAINADMIN_ACCOUNT = ucr.get('tests/domainadmin/account')
if TESTS_DOMAINADMIN_ACCOUNT:
	TESTS_DOMAINADMIN = explodeDn(TESTS_DOMAINADMIN_ACCOUNT, 1)[0]
TESTS_DOMAINADMIN_PWD = ucr.get('tests/domainadmin/pwd')
set_credentials(TESTS_DOMAINADMIN_ACCOUNT, TESTS_DOMAINADMIN_PWD)


def run_smbclient(share, authname, password, smbcmd):
	"""
	runs "smbclient ${share} -U${authname}%${password} -c ${smbcmd}
	and returns the exitcode.
	"""
	cmd = ['smbclient', share, "-U%s%%%s" % (authname, password), "-c", smbcmd]

	return run_cmd(cmd)


def run_cmd(cmd, shell=False, print_output=True):
	try:
		print 'Running %r' % cmd
		p1 = subprocess.Popen(cmd, close_fds=True, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
		(out, err) = p1.communicate()
	except OSError:
		print "Failed to run: %r" % cmd
		raise
	if print_output:
		print out
	return p1.returncode


def access_user_dir(authname, school_id, serverfqdn, username, userfilename, password='univention', testwrite=True, write_access=False, read_access=True):
	roleshare = "//%s/%s" % (serverfqdn, roleshares.roleshare_name(role_pupil, school_id, ucr))
	print "INFO: Testing access to student roleshare for share %s by user %s" % (roleshare, authname)

	smbcmd = "dir %s" % (username,)
	read_success = run_smbclient(roleshare, authname, password, smbcmd) == 0
	if read_success != read_access:
		utils.fail('ERROR: read access test failed: user=%s  share=%s  expected=%s  result=%s' % (authname, roleshare, read_access, read_success))

	if testwrite:
		smbcmd = "del %s\\%s" % (username, userfilename)
		write_success = run_smbclient(roleshare, authname, password, smbcmd) == 0
		if write_success != write_access:
			utils.fail('ERROR: write access test (del) to %s failed: user=%s  share=%s  expected=%s  result=%s' % (userfilename, authname, roleshare, write_access, write_success))

		teacherfilename = "teacherfile1"
		smbcmd = "put /etc/hosts %s\\%s" % (username, teacherfilename)
		write_success = run_smbclient(roleshare, authname, password, smbcmd) == 0
		if write_success != write_access:
			utils.fail('ERROR: write access test (put) to %s failed: user=%s  share=%s  expected=%s  result=%s' % (teacherfilename, authname, roleshare, write_access, write_success))


def create_testfile_on_homeshare(fqdn, username, password, filename):
	share_unc = "//%s/%s" % (fqdn, username)
	print "INFO: Creating testfile %s on home share %s" % (filename, share_unc,)

	smbcmd = "dir"
	if run_smbclient(share_unc, username, password, smbcmd):
		utils.fail('Failed to list home directory')

	smbcmd = "put /etc/hosts %s" % (filename,)
	if run_smbclient(share_unc, username, password, smbcmd):
		utils.fail('Failed to create testfile: %r' % (smbcmd,))


def access_roleshare_student_on_all_school_fileservers(school_id, name_student, name_teacher, name_teacherstaff, name_staff, password='univention', testwrite=True, write_access=False):
	print "INFO: Testing access to student roleshare for school %s" % (school_id,)
	print "INFO: teacher name = %s" % (name_teacher,)
	print "INFO: teacher-staff name = %s" % (name_teacherstaff,)
	print "INFO: staff name = %s" % (name_staff,)
	print "INFO: student name = %s" % (name_student,)
	for fqdn in roleshares.fileservers_for_school(school_id):
		# trigger home creation for stu1 and create a test file
		userfilename = "testfile1"
		create_testfile_on_homeshare(fqdn, name_student, password, userfilename)

		# and test access with teacher
		access_user_dir(name_teacher, school_id, fqdn, name_student, userfilename, testwrite=True, write_access=False, read_access=True)
		# and test access with teacher and staff
		access_user_dir(name_teacherstaff, school_id, fqdn, name_student, userfilename, testwrite=True, write_access=False, read_access=True)
		# and test access with staff
		access_user_dir(name_staff, school_id, fqdn, name_student, userfilename, testwrite=True, write_access=False, read_access=False)
		# and test access with student
		access_user_dir(name_student, school_id, fqdn, name_student, userfilename, testwrite=True, write_access=False, read_access=False)
		# and test access with Administrator
		access_user_dir(TESTS_DOMAINADMIN, school_id, fqdn, name_student, userfilename, testwrite=True, write_access=False, read_access=True)


def smb_server_online(fqdn):
	cmd = ['smbclient', "-N", "-L", fqdn]
	return run_cmd(cmd) == 0


def smb_server_offers_roleshare_student(fqdn, school_id):
	share_name = roleshares.roleshare_name('pupil', school_id, ucr)
	share_unc = "//%s/%s" % (fqdn, share_name)
	return run_smbclient(share_unc, TESTS_DOMAINADMIN, TESTS_DOMAINADMIN_PWD, "quit") == 0


def create_roleshare_student(school_id):
	# create roleshares
	cmd = ["/usr/share/ucs-school-import/scripts/create_roleshares", "--create", "student", "--school", str(school_id), "--binddn", TESTS_DOMAINADMIN_ACCOUNT, "--bindpwd", TESTS_DOMAINADMIN_PWD]
	if run_cmd(cmd):
		utils.fail('Creating student roleshare failed: %r' % (cmd,))


def prepare_fileservers(school_id):
	tested_servers = []
	for fqdn in roleshares.fileservers_for_school(school_id):
		if smb_server_online(fqdn):
			if not smb_server_offers_roleshare_student(fqdn, school_id):
				print "INFO: Manually creating roleshares for %s" % (school_id,)
				create_roleshare_student(school_id)
			tested_servers.append(fqdn)
		else:
			print "WARNING: Server %s is not online" % (fqdn,)
	return tested_servers


@LDAP_Connection(MACHINE_READ)
def main(ldap_machine_read=None):
	status = 100

	if not ucr.is_true('ucsschool/import/roleshare', True):
		utils.fail(log_message="ucsschool/import/roleshare has been deactivated, skipping test", returncode=TestCodes.REASON_SKIP)

	all_available_servers = []
	with testing_ucsschool.UCSTestSchool() as schoolenv:
		ou_A_name, ou_A_dn = schoolenv.create_ou(name_edudc=ucr.get('hostname'))
		ou_B_name, ou_B_dn = schoolenv.create_ou(name_edudc=ucr.get('hostname'))
		ou_C_name, ou_C_dn = schoolenv.create_ou(name_edudc=ucr.get('hostname'))
		for school_id in map(School.get_name_from_dn, [ou_A_dn, ou_B_dn, ou_C_dn]):
			available_servers = prepare_fileservers(school_id)
			if available_servers:
				all_available_servers.extend(available_servers)
			else:
				print "WARNING: No file servers online for school %s" % (school_id,)
				continue

			# create test users
			classes = '{}-1A'.format(school_id)
			tea1, teadn1 = schoolenv.create_user(school_id, classes=classes, is_teacher=True)
			teastaff1, teastaffdn1 = schoolenv.create_user(school_id, classes=classes, is_teacher=True, is_staff=True)
			staff1, staffdn1 = schoolenv.create_user(school_id, classes=classes, is_staff=True)
			stu1, stu1dn = schoolenv.create_user(school_id, classes=classes)

			utils.wait_for_replication_and_postrun()
			print "Waiting for DRS replication..."
			wait_for_drs_replication("(sAMAccountName=%s)" % (escape_filter_chars(stu1),), attrs="objectSid")

			access_roleshare_student_on_all_school_fileservers(school_id, stu1, tea1, teastaff1, staff1)

	if not all_available_servers:
		utils.fail(log_message="No fileserver online", returncode=TestCodes.REASON_FAIL)

	return status


if __name__ == '__main__':
	sys.exit(main())

# vim: set filetype=py
