Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ if [ $MAJ = "0" ];then
mkdir $INSTALL/bin
mkdir $INSTALL/lib
mkdir $INSTALL/ps1_templates
mkdir $INSTALL/ps1_custom_templates
else
mkdir $INSTALL/ps1_custom_templates 2>/dev/null
fi
PWD=`pwd`
chmod 700 ./bin/*
Expand All @@ -35,7 +38,7 @@ ln -s $PWD/bin/resetpwd.py $INSTALL/bin/resetpwd.py 2>/dev/null
ln -s $PWD/bin/delentity.py $INSTALL/bin/delentity.py 2>/dev/null
ln -s $PWD/bin/upsertidentity.py $INSTALL/bin/upsertidentity.py 2>/dev/null
ln -s $PWD/bin/activation.py $INSTALL/bin/activation.py 2>/dev/null
cp -n ./ps1_templates/* $INSTALL/ps1_templates
ln -s $PWD/ps1_templates/* $INSTALL/ps1_templates
chmod 600 $INSTALL/ps1_templates/*
cp config.yml $INSTALL

Expand Down
21 changes: 21 additions & 0 deletions src/bin/lifecycle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/python3 -u
import sys
sys.path.append('../lib')
import ad_utils as ad
import backend_utils as u
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--debug', help='Debug mode', default="")
args = parser.parse_args()

if args.debug == "1" :
ad.__DEBUG__=1
entity=u.readjsoninput()
config=u.read_config('../etc/config.conf')
ad.set_config(config)
if u.is_backend_concerned(entity):
r = ad.lifecycle(entity)
exit(r)
else:
print(u.returncode(0,"not concerned"))
5 changes: 5 additions & 0 deletions src/bin/ping.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
parser.add_argument('--config', help='config file', default="../etc/config.conf")
args = parser.parse_args()
config=u.read_config(args.config)
debug=u.config("debug")
if debug == '2' :
# bypass pour les tests unitaires
print(u.returncode(0, "I m alive"))
exit(0)
ad.set_config(config)
## test connection
exitCode=ad.test_conn()
Expand Down
3 changes: 3 additions & 0 deletions src/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ actions:
PING_TARGET:
script: 'ping.py'
onError: 'stop'
IDENTITY_LIFECYCLE_CHANGED:
script: 'lifecycle.py'
onError: 'stop'
139 changes: 94 additions & 45 deletions src/lib/ad_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
from jinja2 import FileSystemLoader,BaseLoader
import backend_utils as u
import jinja2
import os
__PRIVATE_KEY__ = '../.ssh/id_ed25519'
__TEMPLATES_PS1__ = "../ps1_templates/"
__CUSTOM_TEMPLATES_PS1__ = "../ps1_custom_templates/"
__DEBUG__=0
__TMPDIR__= os.getenv("HOME") + '/tmp'
def set_private_key(keyfile):
global __PRIVATE_KEY__
__PRIVATE_KEY__=keyfile
Expand Down Expand Up @@ -54,7 +57,6 @@ def compose_dn(entity):
}

rdnValue=u.find_key(entity,'cn')
x=type(rdnValue)
if rdnValue is None:
rdnValue='test'
branchAttr=u.config('branchAttr','')
Expand Down Expand Up @@ -87,7 +89,7 @@ def dn_superior(dn):


def test_conn():
environment = jinja2.Environment(loader=FileSystemLoader(__TEMPLATES_PS1__))
environment = jinja2.Environment(loader=FileSystemLoader(get_template_dir('ping.template')))
template = environment.get_template('ping.template')
content=template.render({})
scriptName='ping.ps1'
Expand All @@ -106,64 +108,93 @@ def test_conn():

def gen_script_from_template(entity,template):
dataStatus = 0
type=0
data={}
if 'dataStatus' in entity['payload'].keys():
dataStatus = entity['payload']['dataStatus']
elif 'dataStatus' in entity['payload']['identity']:
dataStatus = entity['payload']['identity']['dataStatus']
data={
'domain' :u.config('domain'),
'base': u.config('base'),
'dn' : compose_dn(entity),
'path': dn_superior(compose_dn(entity)),
'e': u.make_entry_array(entity),
'config': u.get_config(),
'dataStatus' : dataStatus
}
elif 'identity' in entity['payload'].keys():
if 'dataStatus' in entity['payload']['identity']:
dataStatus = entity['payload']['identity']['dataStatus']
elif 'dataStatus' in entity['payload']['identity']['identity']:
dataStatus = entity['payload']['identity']['identity']['dataStatus']
elif 'dataStatus' in entity['payload']['before']:
dataStatus = entity['payload']['before']['dataStatus']
type = 1
if type == 0:
data={
'domain' :u.config('domain'),
'base': u.config('base'),
'dn' : compose_dn(entity),
'path': dn_superior(compose_dn(entity)),
'e': u.make_entry_array(entity),
'config': u.get_config(),
'dataStatus' : dataStatus
}
else:
data = {
'domain': u.config('domain'),
'base': u.config('base'),
'dn': compose_dn(entity),
'path': dn_superior(compose_dn(entity)),
'e': u.make_entry_array(entity,'after'),
'before': u.make_entry_array(entity),
'config': u.get_config(),
'dataStatus': dataStatus
}

environment = jinja2.Environment(loader=FileSystemLoader(__TEMPLATES_PS1__))
environment = jinja2.Environment(loader=FileSystemLoader(get_template_dir(template)))
template = environment.get_template(template)
content=template.render(data)
return content

def ad_exec_script(entity,template,params=""):
if u.config('debug',0) == "1":
__DEBUG__ = 1
elif u.config('debug',0) == "2":
__DEBUG__ = 2
else:
__DEBUG__ = 0
content=gen_script_from_template(entity,template)
client = open_ssh_conn()
sshfile = client.open_sftp()
pid=os.getpid()
if __DEBUG__ == 0 :
scriptName='sesame_script.' + str(pid) + '.ps1'
if (__DEBUG__ == 2):
scriptName = __TMPDIR__ + "/" + os.path.splitext(os.path.basename(sys.argv[0]))[0] + ".ps1"
with open(scriptName, "a") as f:
f.write(content)
print(u.returncode(0, "Backend in debug mode"))
return (0)
else:
scriptName = os.path.splitext(os.path.basename(sys.argv[0]))[0] + ".ps1"
with sshfile.open(scriptName, mode="w") as message:
message.write(content)
##execution du script
chan = client.get_transport().open_session()
if params == '':
cmd=scriptName
else:
cmd=scriptName + " " + params
if __DEBUG__ == 0 :
chan.exec_command('powershell -ExecutionPolicy Bypass -NonInteractive -File ' + cmd)
exitCode = chan.recv_exit_status()
content = chan.recv(4096).decode()
error = chan.recv_stderr(4096).decode()
client = open_ssh_conn()
sshfile = client.open_sftp()
pid=os.getpid()
if __DEBUG__ == 0 :
scriptName='sesame_script.' + str(pid) + '.ps1'
else:
scriptName = os.path.splitext(os.path.basename(sys.argv[0]))[0] + ".ps1"
with sshfile.open(scriptName, mode="w") as message:
message.write(content)
##execution du script
chan = client.get_transport().open_session()
##supression du script
chan.exec_command('del ' + scriptName)
del client
if exitCode == 0:
print(u.returncode(0,content.rstrip("\n")))
return(0)
if params == '':
cmd=scriptName
else:
print(u.returncode(1,content.rstrip("\n")))
return(1)
else:
print(u.returncode(0, "Backend in debug mode"))
return(0)
cmd=scriptName + " " + params
if __DEBUG__ == 0 :
chan.exec_command('powershell -ExecutionPolicy Bypass -NonInteractive -File ' + cmd)
exitCode = chan.recv_exit_status()
content = chan.recv(4096).decode()
error = chan.recv_stderr(4096).decode()
chan = client.get_transport().open_session()
##supression du script
chan.exec_command('del ' + scriptName)
del client
if exitCode == 0:
print(u.returncode(0,content.rstrip("\n")))
return(0)
else:
print(u.returncode(1,content.rstrip("\n")))
return(1)
else:
print(u.returncode(0, "Backend in debug mode"))
return(0)

def ad_exec_script_content(entity,template,params=""):
if u.config('debug',0) == "1":
Expand Down Expand Up @@ -205,4 +236,22 @@ def change_password(entity):
r=ad_exec_script(entity, 'changepassword.template',
"-user " + entity['payload']['uid'] + ' -oldp "' + entity['payload']['oldPassword'] + '" -newp "' +
entity['payload']['newPassword'] + '"')
return(r)
return(r)

def lifecycle(entity):
before = entity['payload']['before']['lifecycle']
after = entity['payload']['after']['lifecycle']
template_name=before + "_" + after + ".template"
test=os.getcwd()
r=0
if os.path.exists(__CUSTOM_TEMPLATES_PS1__ + "/" + template_name):
r=ad_exec_script(entity, template_name)
elif os.path.exists(__CUSTOM_TEMPLATES_PS1__ + "/" +"lifecycle.template"):
r=ad_exec_script(entity, "lifecycle.template")
return(r)

def get_template_dir(template):
if os.path.exists(__CUSTOM_TEMPLATES_PS1__ + '/' + template):
return(__CUSTOM_TEMPLATES_PS1__)
else:
return(__TEMPLATES_PS1__)
15 changes: 13 additions & 2 deletions src/lib/backend_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def _finditem(obj, key):
if item is not None:
return item

def make_entry_array(entity):
def make_entry_array(entity,key='before'):
data = {}
if "identity" in entity['payload']:
objectclasses = entity['payload']['identity']['identity']['additionalFields']['objectClasses']
Expand All @@ -91,7 +91,17 @@ def make_entry_array(entity):
additionalFields = entity['payload']['identity']['identity']['additionalFields']['attributes']
else:
additionalFields = {}

elif key in entity['payload']:
# cas cycle de vie
objectclasses = entity['payload'][key]['additionalFields']['objectClasses']
inetOrgPerson = entity['payload'][key]['inetOrgPerson']
# ajout lifecyle car en dehors du tableau
inetOrgPerson['lifecycle'] = entity['payload'][key]['lifecycle']
addFieldsDict = entity['payload'][key]['additionalFields']
if 'attributes' in addFieldsDict:
additionalFields = entity['payload'][key]['additionalFields']['attributes']
else:
additionalFields = {}
else:
objectclasses = entity['payload']['additionalFields']['objectClasses']
inetOrgPerson = entity['payload']['inetOrgPerson']
Expand All @@ -114,6 +124,7 @@ def make_entry_array(entity):
return data



def make_objectclass(entity,entry):
data = {}
objectclasses=[]
Expand Down
33 changes: 26 additions & 7 deletions unittest/adBinTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import subprocess
import os
import json
import shutil
__PYTHONENV__='/../.venv/bin/python'
class adBinTest (unittest.TestCase):
def run_backend(self,script, file= "",args=""):
Expand All @@ -21,6 +22,7 @@ def run_backend(self,script, file= "",args=""):
content = fic.read()
fic.close()
os.chdir('../src/bin')
content=content.replace("\n", "")
ret = subprocess.run(execargs,input=content.encode(),capture_output=True)
os.chdir(dir)
return { "returncode" : ret.returncode,"stdout" : ret.stdout.decode()}
Expand All @@ -41,14 +43,14 @@ def test_03delentity(self):
self.assertEqual(ret['returncode'], 0)
result = json.loads(ret["stdout"])
self.assertEqual(result["status"], 0)
self.assertEqual(result["message"], "user deleted")
#self.assertEqual(result["message"], "user deleted")

def test_04upsertidentity_add(self):
ret = self.run_backend('upsertidentity.py', './files_ad_utils/identity1.json')
self.assertEqual(ret['returncode'], 0)
result = json.loads(ret["stdout"])
self.assertEqual(result["status"], 0)
self.assertEqual(result["message"], "Identity created")
#self.assertEqual(result["message"], "Identity created")

def test_04upsertidentity_mod(self):
ret = self.run_backend('upsertidentity.py', './files_ad_utils/identity1.json')
Expand All @@ -62,34 +64,34 @@ def test_05init_password(self):
self.assertEqual(ret['returncode'], 0)
result = json.loads(ret["stdout"])
self.assertEqual(result["status"], 0)
self.assertEqual(result["message"], "password reseted")
#self.assertEqual(result["message"], "password reseted")
def test_06change_password(self):
ret = self.run_backend('changepwd.py', './files_ad_utils/changepassword_true.json')
self.assertEqual(ret['returncode'], 0)
result = json.loads(ret["stdout"])
self.assertEqual(result["status"], 0)
self.assertEqual(result["message"], "Password changed")
#self.assertEqual(result["message"], "Password changed")

def test_07change_bad_password(self):
ret = self.run_backend('changepwd.py', './files_ad_utils/changepassword_true.json')
self.assertEqual(ret['returncode'], 1)
result = json.loads(ret["stdout"])
self.assertEqual(result["status"], 1)
self.assertEqual(result["message"], "Authentication Invalid password")
#self.assertEqual(result["message"], "Authentication Invalid password")

def test_08desactivate(self):
ret = self.run_backend('activation.py', './files_ad_utils/identity1.json',"--active=1")
self.assertEqual(ret['returncode'], 0)
result = json.loads(ret["stdout"])
self.assertEqual(result["status"], 0)
self.assertEqual(result["message"], "user Enabled")
#self.assertEqual(result["message"], "user Enabled")

def test_09activate(self):
ret = self.run_backend('activation.py', './files_ad_utils/identity1.json',"--active=0")
self.assertEqual(ret['returncode'], 0)
result = json.loads(ret["stdout"])
self.assertEqual(result["status"], 0)
self.assertEqual(result["message"], "user Disabled")
#self.assertEqual(result["message"], "user Disabled")

def test_10upsertidentify_notconcerned(self):
ret = self.run_backend('upsertidentity.py', './files_ad_utils/identity_notconcerned.json')
Expand Down Expand Up @@ -125,6 +127,23 @@ def test_14desactivate_notconcerned(self):
result = json.loads(ret["stdout"])
self.assertEqual(result["status"], 0)
self.assertEqual(result["message"], "not concerned")
def test_15lifecycle_without_script(self):
ret = self.run_backend('lifecycle.py', './files_ad_utils/lifecycle.json')
self.assertEqual(ret['returncode'], 0)
def test_16lifecycle_notconcerned(self):
ret = self.run_backend('lifecycle.py', './files_ad_utils/lifecycle-notconcerned.json')
self.assertEqual(ret['returncode'], 0)
result = json.loads(ret["stdout"])
self.assertEqual(result["status"], 0)
self.assertEqual(result["message"], "not concerned")

def test_16lifecycle_cyclelife(self):
test=os.getcwd()
ok=shutil.copy('files_ad_utils/lifecycle_test_templates/lifecycle.template',
'../src/ps1_custom_templates/lifecycle.template')
ret = self.run_backend('lifecycle.py', './files_ad_utils/lifecycle.json')
self.assertEqual(ret['returncode'], 0)
os.remove('../src/ps1_custom_templates/lifecycle.template')

if __name__ == '__main__':
unittest.main()
Loading