diff --git a/config/ahenk.conf b/config/ahenk.conf index 66b1c4e..05ac580 100644 --- a/config/ahenk.conf +++ b/config/ahenk.conf @@ -7,14 +7,14 @@ pluginfolderpath = /usr/share/ahenk/plugins/ mainmodulename = main [CONNECTION] -uid = -password = +uid = +password = host = port = 5222 use_tls = false receiverjid = lider_sunucu receiverresource = -servicename = +servicename = im.liderahenk.org receivefileparam = /tmp/ [SESSION] @@ -26,4 +26,3 @@ get_policy_timeout = 30 type = default agreement = 2 user_disabled = false - diff --git a/debian/ahenk.install b/debian/ahenk.install new file mode 100644 index 0000000..73a325f --- /dev/null +++ b/debian/ahenk.install @@ -0,0 +1,300 @@ +etc/logrotate.d/ahenk +etc/logrotate.d +etc/init.d/ahenk +etc/init.d +etc/ahenk/ahenk.conf +etc/ahenk/log.conf +etc/ahenk +etc +usr/share/libpam-script/pam_script_ses_open +usr/share/libpam-script/pam_script_ses_close +usr/share/libpam-script +usr/share/ahenk/base/util/util.py +usr/share/ahenk/base/util +usr/share/ahenk/base/model/profile.py +usr/share/ahenk/base/model/profile_bean.py +usr/share/ahenk/base/model/task.py +usr/share/ahenk/base/model/enum/message_type.py +usr/share/ahenk/base/model/enum/content_type.py +usr/share/ahenk/base/model/enum/__init__.py +usr/share/ahenk/base/model/enum/message_code.py +usr/share/ahenk/base/model/enum +usr/share/ahenk/base/model/task_bean.py +usr/share/ahenk/base/model/plugin.py +usr/share/ahenk/base/model/__init__.py +usr/share/ahenk/base/model/message_factory.py +usr/share/ahenk/base/model/policy.py +usr/share/ahenk/base/model/response.py +usr/share/ahenk/base/model/modes/logout_mode.py +usr/share/ahenk/base/model/modes/safe_mode.py +usr/share/ahenk/base/model/modes/__init__.py +usr/share/ahenk/base/model/modes/init_mode.py +usr/share/ahenk/base/model/modes/shutdown_mode.py +usr/share/ahenk/base/model/modes/login_mode.py +usr/share/ahenk/base/model/modes +usr/share/ahenk/base/model/plugin_bean.py +usr/share/ahenk/base/model/policy_bean.py +usr/share/ahenk/base/model +usr/share/ahenk/base/timer/timer.py +usr/share/ahenk/base/timer/setup_timer.py +usr/share/ahenk/base/timer +usr/share/ahenk/base/config/config_manager.py +usr/share/ahenk/base/config/__init__.py +usr/share/ahenk/base/config +usr/share/ahenk/base/default_policy/config-files/xfce4-notifyd.xml +usr/share/ahenk/base/default_policy/config-files +usr/share/ahenk/base/default_policy/default_policy.py +usr/share/ahenk/base/default_policy/__init__.py +usr/share/ahenk/base/default_policy +usr/share/ahenk/base/deamon/__init__.py +usr/share/ahenk/base/deamon/base_daemon.py +usr/share/ahenk/base/deamon +usr/share/ahenk/base/mail/mail_manager.py +usr/share/ahenk/base/mail +usr/share/ahenk/base/messaging/__init__.py +usr/share/ahenk/base/messaging/anonymous_messenger.py +usr/share/ahenk/base/messaging/messaging.py +usr/share/ahenk/base/messaging/message_response_queue.py +usr/share/ahenk/base/messaging/messenger.py +usr/share/ahenk/base/messaging +usr/share/ahenk/base/task/task_in_queue.py +usr/share/ahenk/base/task/__init__.py +usr/share/ahenk/base/task/task_job.py +usr/share/ahenk/base/task/task_manager.py +usr/share/ahenk/base/task +usr/share/ahenk/base/command/command_runner.py +usr/share/ahenk/base/command/command_manager.py +usr/share/ahenk/base/command/fifo.py +usr/share/ahenk/base/command +usr/share/ahenk/base/__init__.py +usr/share/ahenk/base/event/event_base.py +usr/share/ahenk/base/event/event_manager.py +usr/share/ahenk/base/event +usr/share/ahenk/base/agreement/confirm.py +usr/share/ahenk/base/agreement/agreement.py +usr/share/ahenk/base/agreement/ahenkmessage.py +usr/share/ahenk/base/agreement/ask.py +usr/share/ahenk/base/agreement/unregistrationmessage.py +usr/share/ahenk/base/agreement +usr/share/ahenk/base/plugin/abstract_plugin.py +usr/share/ahenk/base/plugin/plugin_queue.py +usr/share/ahenk/base/plugin/plugin_manager_factory.py +usr/share/ahenk/base/plugin/plugin.py +usr/share/ahenk/base/plugin/__init__.py +usr/share/ahenk/base/plugin/plugin_manager.py +usr/share/ahenk/base/plugin/file_handler.py +usr/share/ahenk/base/plugin/plugin_install_listener.py +usr/share/ahenk/base/plugin +usr/share/ahenk/base/file/ssh_file_transfer.py +usr/share/ahenk/base/file/http_file_transfer.py +usr/share/ahenk/base/file/file_transfer_manager.py +usr/share/ahenk/base/file +usr/share/ahenk/base/scope.py +usr/share/ahenk/base/execution/__init__.py +usr/share/ahenk/base/execution/execution_manager.py +usr/share/ahenk/base/execution +usr/share/ahenk/base/database/ahenk_db_service.py +usr/share/ahenk/base/database +usr/share/ahenk/base/logger/__init__.py +usr/share/ahenk/base/logger/ahenk_logger.py +usr/share/ahenk/base/logger +usr/share/ahenk/base/scheduler/scheduler_factory.py +usr/share/ahenk/base/scheduler/base_scheduler.py +usr/share/ahenk/base/scheduler/__init__.py +usr/share/ahenk/base/scheduler/custom/schedule_job.py +usr/share/ahenk/base/scheduler/custom/all_match.py +usr/share/ahenk/base/scheduler/custom/__init__.py +usr/share/ahenk/base/scheduler/custom/scheduledb.py +usr/share/ahenk/base/scheduler/custom/custom_scheduler.py +usr/share/ahenk/base/scheduler/custom +usr/share/ahenk/base/scheduler +usr/share/ahenk/base/registration/test.py +usr/share/ahenk/base/registration/execute_cancel_sssd_ad_authentication.py +usr/share/ahenk/base/registration/config-files/ldap +usr/share/ahenk/base/registration/config-files/pam_script +usr/share/ahenk/base/registration/config-files/krb5.conf +usr/share/ahenk/base/registration/config-files/sssd_ad.conf +usr/share/ahenk/base/registration/config-files/sssd.conf +usr/share/ahenk/base/registration/config-files +usr/share/ahenk/base/registration/execute_ldap_login.py +usr/share/ahenk/base/registration/execute_sssd_authentication.py +usr/share/ahenk/base/registration/__init__.py +usr/share/ahenk/base/registration/execute_sssd_ad_authentication.py +usr/share/ahenk/base/registration/registration.py +usr/share/ahenk/base/registration/execute_cancel_ldap_login.py +usr/share/ahenk/base/registration/scripts/ldap-login.sh +usr/share/ahenk/base/registration/scripts/ad.sh +usr/share/ahenk/base/registration/scripts +usr/share/ahenk/base/registration/execute_cancel_sssd_authentication.py +usr/share/ahenk/base/registration +usr/share/ahenk/base/system/system.py +usr/share/ahenk/base/system +usr/share/ahenk/base +usr/share/ahenk/__init__.py +usr/share/ahenk/ahenkd.py +usr/share/ahenk/helper/__init__.py +usr/share/ahenk/helper/system.py +usr/share/ahenk/helper +usr/share/ahenk/plugins/network-manager/delete_network.py +usr/share/ahenk/plugins/network-manager/delete_domain.py +usr/share/ahenk/plugins/network-manager/allow_port.py +usr/share/ahenk/plugins/network-manager/add_host.py +usr/share/ahenk/plugins/network-manager/add_network.py +usr/share/ahenk/plugins/network-manager/main.py +usr/share/ahenk/plugins/network-manager/get_network_information.py +usr/share/ahenk/plugins/network-manager/delete_dns.py +usr/share/ahenk/plugins/network-manager/add_dns.py +usr/share/ahenk/plugins/network-manager/block_port.py +usr/share/ahenk/plugins/network-manager/delete_host.py +usr/share/ahenk/plugins/network-manager/add_domain.py +usr/share/ahenk/plugins/network-manager/change_hostname.py +usr/share/ahenk/plugins/network-manager +usr/share/ahenk/plugins/ldap/init.py +usr/share/ahenk/plugins/ldap/safe.py +usr/share/ahenk/plugins/ldap/login.py +usr/share/ahenk/plugins/ldap/main.py +usr/share/ahenk/plugins/ldap/move_agent.py +usr/share/ahenk/plugins/ldap/delete_agent.py +usr/share/ahenk/plugins/ldap/policy.py +usr/share/ahenk/plugins/ldap/task_command_id.py +usr/share/ahenk/plugins/ldap/rename_entry.py +usr/share/ahenk/plugins/ldap/shutdown.py +usr/share/ahenk/plugins/ldap/logout.py +usr/share/ahenk/plugins/ldap +usr/share/ahenk/plugins/service/service_management.py +usr/share/ahenk/plugins/service/init.py +usr/share/ahenk/plugins/service/service_list.py +usr/share/ahenk/plugins/service/main.py +usr/share/ahenk/plugins/service/get_services.py +usr/share/ahenk/plugins/service +usr/share/ahenk/plugins/resource-usage/main.py +usr/share/ahenk/plugins/resource-usage/resource_info_alert.py +usr/share/ahenk/plugins/resource-usage/send_mail.py +usr/share/ahenk/plugins/resource-usage/resource_info_fetcher.py +usr/share/ahenk/plugins/resource-usage/shutdown.py +usr/share/ahenk/plugins/resource-usage +usr/share/ahenk/plugins/sudoers/safe.py +usr/share/ahenk/plugins/sudoers/main.py +usr/share/ahenk/plugins/sudoers/policy.py +usr/share/ahenk/plugins/sudoers +usr/share/ahenk/plugins/rsyslog/main.py +usr/share/ahenk/plugins/rsyslog/policy.py +usr/share/ahenk/plugins/rsyslog +usr/share/ahenk/plugins/disk-quota/init.py +usr/share/ahenk/plugins/disk-quota/safe.py +usr/share/ahenk/plugins/disk-quota/main.py +usr/share/ahenk/plugins/disk-quota/policy.py +usr/share/ahenk/plugins/disk-quota/get_quota.py +usr/share/ahenk/plugins/disk-quota/line.py +usr/share/ahenk/plugins/disk-quota/fstab.py +usr/share/ahenk/plugins/disk-quota/api/disk_quota.py +usr/share/ahenk/plugins/disk-quota/api/disk_quota_ltsp.py +usr/share/ahenk/plugins/disk-quota/api +usr/share/ahenk/plugins/disk-quota +usr/share/ahenk/plugins/network-inventory/main.py +usr/share/ahenk/plugins/network-inventory/multiple-file-transfer.py +usr/share/ahenk/plugins/network-inventory/scannetwork.py +usr/share/ahenk/plugins/network-inventory/installahenk.py +usr/share/ahenk/plugins/network-inventory +usr/share/ahenk/plugins/conky/main.py +usr/share/ahenk/plugins/conky/policy.py +usr/share/ahenk/plugins/conky/execute_conky.py +usr/share/ahenk/plugins/conky/execute_xmessage.py +usr/share/ahenk/plugins/conky/ask.py +usr/share/ahenk/plugins/conky +usr/share/ahenk/plugins/local-user/panelconf/xfce4-panel.xml +usr/share/ahenk/plugins/local-user/panelconf +usr/share/ahenk/plugins/local-user/init.py +usr/share/ahenk/plugins/local-user/main.py +usr/share/ahenk/plugins/local-user/delete_user.py +usr/share/ahenk/plugins/local-user/get_groups.py +usr/share/ahenk/plugins/local-user/add_user.py +usr/share/ahenk/plugins/local-user/edit_user.py +usr/share/ahenk/plugins/local-user/scripts/find_locked_users.sh +usr/share/ahenk/plugins/local-user/scripts/remove_locked_users.sh +usr/share/ahenk/plugins/local-user/scripts +usr/share/ahenk/plugins/local-user/get_users.py +usr/share/ahenk/plugins/local-user +usr/share/ahenk/plugins/file-management/main.py +usr/share/ahenk/plugins/file-management/write_to_file.py +usr/share/ahenk/plugins/file-management/get_file_content.py +usr/share/ahenk/plugins/file-management +usr/share/ahenk/plugins/ldap-login/init.py +usr/share/ahenk/plugins/ldap-login/main.py +usr/share/ahenk/plugins/ldap-login/execute_ldap_login.py +usr/share/ahenk/plugins/ldap-login/execute_cancel_ldap_login.py +usr/share/ahenk/plugins/ldap-login/execute_ad_login.py +usr/share/ahenk/plugins/ldap-login +usr/share/ahenk/plugins/browser/main.py +usr/share/ahenk/plugins/browser/policy.py +usr/share/ahenk/plugins/browser +usr/share/ahenk/plugins/usb/manage-usb.py +usr/share/ahenk/plugins/usb/init.py +usr/share/ahenk/plugins/usb/main.py +usr/share/ahenk/plugins/usb/policy.py +usr/share/ahenk/plugins/usb/scripts/ENABLED_webcam.sh +usr/share/ahenk/plugins/usb/scripts/DISABLED_usbhid.sh +usr/share/ahenk/plugins/usb/scripts/DISABLED_webcam.sh +usr/share/ahenk/plugins/usb/scripts/ENABLED_printer.sh +usr/share/ahenk/plugins/usb/scripts/ENABLED_usbhid.sh +usr/share/ahenk/plugins/usb/scripts/DISABLED_printer.sh +usr/share/ahenk/plugins/usb/scripts/ENABLED_usbstorage.sh +usr/share/ahenk/plugins/usb/scripts/DISABLED_usbstorage.sh +usr/share/ahenk/plugins/usb/scripts +usr/share/ahenk/plugins/usb/logout.py +usr/share/ahenk/plugins/usb +usr/share/ahenk/plugins/remote-access/main.py +usr/share/ahenk/plugins/remote-access/setup-vnc-server.py +usr/share/ahenk/plugins/remote-access +usr/share/ahenk/plugins/package-manager/init.py +usr/share/ahenk/plugins/package-manager/get_execution_info.py +usr/share/ahenk/plugins/package-manager/repositories.py +usr/share/ahenk/plugins/package-manager/main.py +usr/share/ahenk/plugins/package-manager/package_sources.py +usr/share/ahenk/plugins/package-manager/show_package_archive.py +usr/share/ahenk/plugins/package-manager/installed_packages.py +usr/share/ahenk/plugins/package-manager/package_management.py +usr/share/ahenk/plugins/package-manager/package_archive.py +usr/share/ahenk/plugins/package-manager/scripts/sourcelist.sh +usr/share/ahenk/plugins/package-manager/scripts +usr/share/ahenk/plugins/package-manager/check_package.py +usr/share/ahenk/plugins/package-manager/packages.py +usr/share/ahenk/plugins/package-manager +usr/share/ahenk/plugins/user-privilege/init.py +usr/share/ahenk/plugins/user-privilege/safe.py +usr/share/ahenk/plugins/user-privilege/main.py +usr/share/ahenk/plugins/user-privilege/policy.py +usr/share/ahenk/plugins/user-privilege/shutdown.py +usr/share/ahenk/plugins/user-privilege +usr/share/ahenk/plugins/manage-root/init.py +usr/share/ahenk/plugins/manage-root/safe.py +usr/share/ahenk/plugins/manage-root/login.py +usr/share/ahenk/plugins/manage-root/main.py +usr/share/ahenk/plugins/manage-root/policy.py +usr/share/ahenk/plugins/manage-root/shutdown.py +usr/share/ahenk/plugins/manage-root/set_root_password.py +usr/share/ahenk/plugins/manage-root/logout.py +usr/share/ahenk/plugins/manage-root +usr/share/ahenk/plugins/script/main.py +usr/share/ahenk/plugins/script/policy.py +usr/share/ahenk/plugins/script/execute_script.py +usr/share/ahenk/plugins/script +usr/share/ahenk/plugins/login-manager/init.py +usr/share/ahenk/plugins/login-manager/safe.py +usr/share/ahenk/plugins/login-manager/main.py +usr/share/ahenk/plugins/login-manager/policy.py +usr/share/ahenk/plugins/login-manager/machine_shutdown.py +usr/share/ahenk/plugins/login-manager/scripts/cron.sh +usr/share/ahenk/plugins/login-manager/scripts/check.py +usr/share/ahenk/plugins/login-manager/scripts +usr/share/ahenk/plugins/login-manager/manage.py +usr/share/ahenk/plugins/login-manager/shutdown.py +usr/share/ahenk/plugins/login-manager +usr/share/ahenk/plugins +usr/share/ahenk/api/service/ps_util.py +usr/share/ahenk/api/service +usr/share/ahenk/api +usr/share/ahenk +usr/share +usr + diff --git a/debian/ahenk.postinst b/debian/ahenk.postinst new file mode 100644 index 0000000..8e276dc --- /dev/null +++ b/debian/ahenk.postinst @@ -0,0 +1,15 @@ +#!/bin/sh + +set -e + +#mkdir -p /usr/share/ahenk/plugins + +# update ahenk from 1.0.0-6 to 1.0.0-7 +if [ ! -d /etc/ahenk ]; then + + mkdir -p /etc/ahenk + cp -rf /tmp/ahenk/* /etc/ahenk +fi + +systemctl enable ahenk +systemctl start ahenk diff --git a/debian/ahenk.postinst.debhelper b/debian/ahenk.postinst.debhelper new file mode 100644 index 0000000..842fa34 --- /dev/null +++ b/debian/ahenk.postinst.debhelper @@ -0,0 +1,15 @@ + +# Automatically added by dh_python3: +if which py3compile >/dev/null 2>&1; then + py3compile -p ahenk /usr/share/ahenk -V 3.2- +fi + +# End automatically added section +# Automatically added by dh_installinit +if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ]; then + if [ -x "/etc/init.d/ahenk" ]; then + update-rc.d ahenk defaults >/dev/null + invoke-rc.d ahenk start || exit $? + fi +fi +# End automatically added section diff --git a/debian/ahenk.postrm b/debian/ahenk.postrm new file mode 100644 index 0000000..d6231ee --- /dev/null +++ b/debian/ahenk.postrm @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +if [ -d /etc/ahenk ] && [ "$1" = "purge" ];then + rm -rf /etc/ahenk +fi diff --git a/debian/ahenk.postrm.debhelper b/debian/ahenk.postrm.debhelper new file mode 100644 index 0000000..e3de6e3 --- /dev/null +++ b/debian/ahenk.postrm.debhelper @@ -0,0 +1,12 @@ +# Automatically added by dh_installinit +if [ "$1" = "purge" ] ; then + update-rc.d ahenk remove >/dev/null +fi + + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [ -d /run/systemd/system ] ; then + systemctl --system daemon-reload >/dev/null || true +fi +# End automatically added section diff --git a/debian/ahenk.preinst b/debian/ahenk.preinst new file mode 100644 index 0000000..1f60801 --- /dev/null +++ b/debian/ahenk.preinst @@ -0,0 +1,14 @@ +#!/bin/sh + +set -e + +# Create necessary directories +#update ahenk from 1.0.0-6 to 1.0.0-7 +if [ -d /etc/ahenk ]; then + + cp -rf /etc/ahenk /tmp + +else + + mkdir -p /etc/ahenk +fi diff --git a/debian/ahenk.service b/debian/ahenk.service new file mode 100644 index 0000000..420dc39 --- /dev/null +++ b/debian/ahenk.service @@ -0,0 +1,13 @@ +[Unit] +Description=Starts Ahenk at system startup +After=network.target + +[Service] +Type=simple +ExecStart=/usr/bin/python3 /usr/share/ahenk/ahenkd.py start +ExecStop=/usr/bin/python3 /usr/share/ahenk/ahenkd.py stop +PIDFile=/var/run/ahenkd.pid +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/debian/ahenk_init b/debian/ahenk_init new file mode 100644 index 0000000..15d1417 --- /dev/null +++ b/debian/ahenk_init @@ -0,0 +1,45 @@ +#! /bin/bash +### BEGIN INIT INFO +# Provides: ahenk +# Required-Start: $remote_fs $syslog $network +# Required-Stop: $remote_fs $syslog $network +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Manages ahenk service. +# Description: Debian init script for the ahenk executables +# scheduler +### END INIT INFO +# +# Author: S Suleyman Arslan +# + +# Activate the python virtual environment +# . /path_to_virtualenv/activate +case "$1" in + start) + echo "Starting server" + # Start the daemon + #python $AHENKDPATH start + systemctl start ahenk.service + ;; + stop) + echo "Stopping server" + systemctl stop ahenk.service + ;; + restart) + echo "Restarting server" + systemctl restart ahenk.service + ;; + status) + echo "Server Status" + # Status of the daemon + systemctl status ahenk.service + ;; + *) + # Refuse to do other stuff + echo "Usage: /etc/init.d/ahenk.sh {start|stop|restart|status}" + exit 1 + ;; +esac + +exit 0 \ No newline at end of file diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..d4d104e --- /dev/null +++ b/debian/changelog @@ -0,0 +1,188 @@ +ahenk (1.0.0-9) unstable; urgency=medium + + * added debian folder + * added debian folder + * updated changelog file + * added dep network-inventory plugin + * updated changelog file + * synced to development branch + + -- Tuncay ÇOLAK Tue, 15 Oct 2019 10:37:55 +0300 + +ahenk (1.0.0-8) unstable; urgency=medium + + [ Gökhan Gurbetoğlu ] + * Chronological order of roadmap + + [ Tuncay ÇOLAK ] + * added method to delete local users after registration ahenk + * added method to disabled local users after registration ahenk + * registration with hostname + + [ Cihangir Akturk ] + * Use std library whenever possible + + [ Tuncay ÇOLAK ] + * changed local user name and home directory name when registration agent + * changed local user name and home directory name when registration agent + * bugfix + + [ Ömer ÇAKMAK ] + * receiverjid and agreement default values changed + + [ root ] + * Method for getting device language and getting computer model for showing Ahenk informations on Lider Console are added to core. + + [ hasankara ] + * Method for getting device language and getting computer model for showing Ahenk informations on Lider Console are added to core. + * tab is removed + + [ Tuncay ÇOLAK ] + * killall process of local user + + [ hasankara ] + * Update registration.py + * missing header locale is added to util.py + + [ Tuncay ÇOLAK ] + * enabled local user when unregistration agent + + [ edip ] + * Ahenk Registration for user authorization.. + * registarition bugfix + * register and unregister from user gui + * register bugfix + + [ Edip YILDIZ ] + * Update util.py + + [ edip ] + * disable user change + * disable user + * disable user config set + * bugfix + * registration attemp + * bugfix + * sdf + * bugfix + * unregistariton message fixed + * unregister bugfix + * unregister message for user display + * unregister show message fixed + * lider messages changed + * unregister + * ldap config check user and server + * registrtion db change user table for session + * polkit issues + * registration for cache + * check message file + * registration add util methods + * adding config for cache + * pam config change for agent id + * pam ldap login and cancel operations are moved to registration module + + [ Tuncay ÇOLAK ] + * script executable ldap-login.sh + + [ edip ] + * add user info for registiration process + + [ Hasan Kara ] + * SSSD config and installation python files are copied under registration + + [ edip ] + * registrarion for sssd + + [ Hasan Kara ] + * firefox autostart is added if profile is not created for user. + + [ edip ] + * change log + + [ hasankara ] + * sssd bug has been solved for clients which has language turkish + + [ Tuncay ÇOLAK ] + * add user mode changed to 0700 in file /etc/adduser.conf + * sssd configuraton for ldap login + + [ Hasan Kara ] + * sudo role refresh time is set to 1 sec + + [ Tuncay ÇOLAK ] + * added polkit file + + [ Hasan Kara ] + * restarting sssd service is added to login method + * sssd ldap full and smart refresh times has been edited + * sssd ldap_sudo_search_base has been made dynamic + + [ Tuncay ÇOLAK ] + * added default policy for users + * added xfce4-notifyd.xml template file and set owner and group user's .config file + * set display to messsages.py + * set offline_credentials_expiration time and set display unregister ui + * send display parameter to unregister_message + * added get user display number methode + * created autostart file for firefox, firefox-esr and iceweasel when user first login + * root password removed from Receşved message + * bugfix: get display number + * bugfix: logging set on message type + * added debian folder + * added debian folder + * updated changelog file + * added dep network-inventory plugin + * synced to development branch + + -- Tuncay ÇOLAK Tue, 15 Oct 2019 09:47:28 +0300 + +ahenk (1.0.0-7) unstable; urgency=medium + + [ Ömer Çakmak ] + * Add dependency python3-easygui + * Changed to package installer dpkg + * the agent conf file is deleted while the agent is purged + * update agent from 1.0.0-6 to 1.0.0-7 + + [ Tuncay ÇOLAK ] + + -- Tuncay ÇOLAK Wed, 25 Apr 2018 16:05:13 +0300 + +ahenk (1.0.0-6) unstable; urgency=medium + + [ Tuncay ÇOLAK ] + * Add preinst script to fix uninstalled configs + + [ Tuncay ÇOLAK ] + + -- Tuncay ÇOLAK Mon, 12 Feb 2018 11:07:33 +0300 + +ahenk (1.0.0-5) unstable; urgency=medium + + * fix postins syntax + + -- Tuncay ÇOLAK Fri, 24 Nov 2017 10:33:23 +0300 + +ahenk (1.0.0-4) unstable; urgency=medium + + * update postrm script + + -- Tuncay ÇOLAK Fri, 24 Nov 2017 10:21:56 +0300 + +ahenk (1.0.0-3) unstable; urgency=medium + + * udpate control file and postins script for overriding pam script + + -- Tuncay ÇOLAK Fri, 24 Nov 2017 10:04:38 +0300 + +ahenk (1.0.0-2) unstable; urgency=medium + + * Add postinstall and post remove script to take everything under control + + -- Yunusemre Şentürk Wed, 22 Nov 2017 09:59:39 +0300 + +ahenk (1.0.0-1) onyedi; urgency=medium + + * Initial release. + + -- Yunusemre Şentürk Tue, 21 Nov 2017 15:06:11 +0300 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..999e42b --- /dev/null +++ b/debian/control @@ -0,0 +1,42 @@ +Source: ahenk +Section: utils +Priority: optional +Maintainer: İsmail Başaran +Uploaders: Yunusemre Şentürk +Build-Depends: debhelper (>=9), dh-python, python3-all +Standards-Version: 3.9.8 +Homepage: http://www.liderahenk.org.tr +X-Python3-Version: >= 3.2 + +Package: ahenk +Architecture: all +Depends: ${misc:Depends}, + ${python3:Depends}, + python3-cpuinfo, + python3-netifaces, + python3-paramiko, + python3-psutil, + python3-sleekxmpp, + python3-watchdog, + python3-easygui, + libpam-script, + acct, + chkconfig, + x11vnc, + conky, + conky-all, + nmap, + whois, + cpulimit, + policykit-1, + sudo, + rsyslog, + rsyslog-relp, + quota, + quotatool, + ahenk-register +Replaces: libpam-runtime +Description: The client side of the Lider Ahenk Project + Lider Ahenk is an open source project which provides solutions + to manage, monitor and audit unlimited number of different + systems and users on a network. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..7422df4 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,28 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: ahenk +Source: https://github.com/Pardus-LiderAhenk/ahenk + +Files: * +Copyright: 2017 İsmail Başaran +License: GPL-3.0+ + +Files: debian/* +Copyright: 2017 Yunusemre Şentürk +License: GPL-3.0+ + +License: GPL-3.0+ + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/debian/debhelper-build-stamp b/debian/debhelper-build-stamp new file mode 100644 index 0000000..ffcb6d6 --- /dev/null +++ b/debian/debhelper-build-stamp @@ -0,0 +1 @@ +ahenk diff --git a/debian/files b/debian/files new file mode 100644 index 0000000..3012597 --- /dev/null +++ b/debian/files @@ -0,0 +1,2 @@ +ahenk_1.0.0-7.1_all.deb utils optional +ahenk_1.0.0-7.1_amd64.buildinfo utils optional diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000..b09dfb3 --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,19 @@ +# Configuration file for "gbp " + +[DEFAULT] +# the default branch for upstream sources: +upstream-branch = master +# the default branch for the debian patch: +debian-branch = debian +# the default tag formats used: +upstream-tag = %(version)s +debian-tag = debian/%(version)s +# don't check if debian-branch == current branch: +ignore-branch = True +# Use color when on a terminal, alternatives: on/true, off/false or auto +color = auto + +# Options only affecting gbp buildpackage +[buildpackage] +# Look for a tag matching the upstream version when creating a tarball +upstream-tree = tag diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..e69de29 diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..52f596a --- /dev/null +++ b/debian/rules @@ -0,0 +1,6 @@ +#!/usr/bin/make -f +# You must remove unused comment lines for the released package. +#export DH_VERBOSE = 1 + +%: + dh $@ --with python3 diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/source/local-options b/debian/source/local-options new file mode 100644 index 0000000..00131ee --- /dev/null +++ b/debian/source/local-options @@ -0,0 +1,2 @@ +#abort-on-upstream-changes +#unapply-patches diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..9e7c0da --- /dev/null +++ b/debian/watch @@ -0,0 +1 @@ +version=3 diff --git a/etc/ahenk/ahenk.conf b/etc/ahenk/ahenk.conf new file mode 100644 index 0000000..4bbd468 --- /dev/null +++ b/etc/ahenk/ahenk.conf @@ -0,0 +1,28 @@ +[BASE] +logconfigurationfilepath = /etc/ahenk/log.conf +dbpath = /etc/ahenk/ahenk.db + +[PLUGIN] +pluginfolderpath = /usr/share/ahenk/plugins/ +mainmodulename = main + +[CONNECTION] +uid = +password = +host = +port = 5222 +use_tls = false +receiverjid = lider_sunucu +receiverresource = Smack +servicename = im.liderahenk.org +receivefileparam = /tmp/ + +[SESSION] +agreement_timeout = 30 +registration_timeout = 30 +get_policy_timeout = 30 + +[MACHINE] +type = default +agreement = 2 +user_disabled = false diff --git a/etc/ahenk/log.conf b/etc/ahenk/log.conf new file mode 100644 index 0000000..c3727f9 --- /dev/null +++ b/etc/ahenk/log.conf @@ -0,0 +1,23 @@ +[formatters] +keys=default + +[formatter_default] +format=format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s +class=logging.Formatter + +[handlers] +keys=file + +[handler_file] +class=logging.FileHandler +level=DEBUG +formatter=default +args=("/var/log/ahenk.log", "w") + +[loggers] +keys=root + +[logger_root] +level=DEBUG +formatter=default +handlers=file diff --git a/etc/init.d/ahenk b/etc/init.d/ahenk new file mode 100644 index 0000000..15d1417 --- /dev/null +++ b/etc/init.d/ahenk @@ -0,0 +1,45 @@ +#! /bin/bash +### BEGIN INIT INFO +# Provides: ahenk +# Required-Start: $remote_fs $syslog $network +# Required-Stop: $remote_fs $syslog $network +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Manages ahenk service. +# Description: Debian init script for the ahenk executables +# scheduler +### END INIT INFO +# +# Author: S Suleyman Arslan +# + +# Activate the python virtual environment +# . /path_to_virtualenv/activate +case "$1" in + start) + echo "Starting server" + # Start the daemon + #python $AHENKDPATH start + systemctl start ahenk.service + ;; + stop) + echo "Stopping server" + systemctl stop ahenk.service + ;; + restart) + echo "Restarting server" + systemctl restart ahenk.service + ;; + status) + echo "Server Status" + # Status of the daemon + systemctl status ahenk.service + ;; + *) + # Refuse to do other stuff + echo "Usage: /etc/init.d/ahenk.sh {start|stop|restart|status}" + exit 1 + ;; +esac + +exit 0 \ No newline at end of file diff --git a/etc/logrotate.d/ahenk b/etc/logrotate.d/ahenk new file mode 100644 index 0000000..382fed2 --- /dev/null +++ b/etc/logrotate.d/ahenk @@ -0,0 +1,11 @@ +/var/log/ahenk.log { + weekly + missingok + notifempty + rotate 5 + compress + delaycompress + copytruncate + minsize 1000k + create 0766 root root +} diff --git a/src/ahenkd.py b/src/ahenkd.py index 33721bb..9ddab75 100644 --- a/src/ahenkd.py +++ b/src/ahenkd.py @@ -159,10 +159,10 @@ class AhenkDaemon(BaseDaemon): # self.registration_failed() if registration.is_registered() is False: - print("Registation attemp") + print("Registration attemp") max_attempt_number -= 1 self.logger.debug('Ahenk is not registered. Attempting for registration') - registration.registration_request() + registration.registration_request(self.register_hostname,self.register_user_name,self.register_user_password) #if max_attempt_number < 0: # self.logger.warning('Number of Attempting for registration is over') @@ -240,28 +240,33 @@ class AhenkDaemon(BaseDaemon): Util.create_file(System.Ahenk.fifo_file()) Util.set_permission(System.Ahenk.fifo_file(), '600') - def disable_local_users(self): + def set_register_user(self, hostName, username, password): + self.register_hostname=hostName + self.register_user_name=username + self.register_user_password=password + # if user_disabled is when ahenk service restarted TRUE disabled local users + def disable_local_users(self): self.logger.info('Local users disable action start..') conf_manager = Scope.get_instance().get_configuration_manager() if conf_manager.has_section('MACHINE'): user_disabled = conf_manager.get("MACHINE", "user_disabled") self.logger.info('User disabled value=' + str(user_disabled)) - if user_disabled == '0': + if user_disabled == 'true': self.logger.info('local user disabling') Scope.get_instance().get_registration().disable_local_users() - - conf_manager.set('MACHINE', 'user_disabled', '1') + conf_manager.set('MACHINE', 'user_disabled', 'disabled') with open('/etc/ahenk/ahenk.conf', 'w') as configfile: - self.logger.info('oepning config file ') + self.logger.info('opening config file ') conf_manager.write(configfile) user_disabled = conf_manager.get("MACHINE", "user_disabled") self.logger.info('User succesfully disabled value=' + str(user_disabled)) + else: - self.logger.info('users already disabled') + self.logger.info('local users will not be disabled because local_user_paramater is FALSE') def run(self): """ docstring""" @@ -310,7 +315,7 @@ class AhenkDaemon(BaseDaemon): self.check_registration() - #self.is_registered() + self.is_registered() self.disable_local_users() @@ -322,7 +327,8 @@ class AhenkDaemon(BaseDaemon): self.init_signal_listener() self.logger.info('Signals listeners was set') - Agreement().agreement_contract_update() + # Agreement().agreement_contract_update() + global_scope.put_custom_map('ahenk_daemon', ahenk_daemon) self.init_message_response_queue() @@ -343,6 +349,7 @@ if __name__ == '__main__': ahenk_daemon = AhenkDaemon(System.Ahenk.pid_path()) try: if len(sys.argv) == 2 and (sys.argv[1] in ('start', 'stop', 'restart', 'status')): + ahenk_daemon.set_register_user(None, None, None) if sys.argv[1] == 'start': if System.Ahenk.is_running() is True: print('There is already running Ahenk service. It will be killed.[{0}]'.format( @@ -368,6 +375,14 @@ if __name__ == '__main__': else: print('Unknown command. Usage : %s start|stop|restart|status|clean' % sys.argv[0]) sys.exit(2) + elif len(sys.argv) > 2 and (sys.argv[1] in ('register')): + params = sys.argv[1] + hostName = sys.argv[2] + userName = sys.argv[3] + password = sys.argv[4] + ahenk_daemon.set_register_user(hostName,userName,password) + ahenk_daemon.run() + else: result = Commander().set_event(sys.argv) if result is None: diff --git a/src/base/agreement/ahenkmessage.py b/src/base/agreement/ahenkmessage.py index 8fd07df..eedfcb6 100644 --- a/src/base/agreement/ahenkmessage.py +++ b/src/base/agreement/ahenkmessage.py @@ -1,47 +1,86 @@ +# #!/usr/bin/python3 +# -*- coding: utf-8 -*- + +import tkinter as tk +from tkinter import * +import os import sys -from easygui import multpasswordbox, msgbox -def ask(message, title, host): +class AskRegister(): - field_names=[] + message = None + title = None + host = "" - if host =='': - field_names.append("Etki Alanı Sunucusu:") + def __init__(self, message, title, host): - field_names.append("Yetkili Kullanıcı") - field_names.append("Parola") + self.message = message + self.title = title + self.host = host + self.master = tk.Tk() + self.master.title(self.title) - field_values = multpasswordbox( - msg=message, - title=title, fields=(field_names)) + if self.host != "": + pass + else: + tk.Label(self.master, text="Etki Alanı Sunucusu : ").grid(row=0) + self.e1 = tk.Entry(self.master) + self.e1.grid(row=0, column=1) - if field_values is None: - return print('N'); + tk.Label(self.master, text="Yetkili Kullanıcı : ").grid(row=1) + tk.Label(self.master, text="Parola : ").grid(row=2) - is_fieldvalue_empty = False; + self.e2 = tk.Entry(self.master) + self.e3 = tk.Entry(show="*") - for value in field_values: - if value == '': - is_fieldvalue_empty = True; + self.var1 = IntVar() + Checkbutton(self.master, text="Active Directory", variable=self.var1, command=self.check1).grid(row=3, column=0, stick=tk.W, + pady=4) + self.var2 = IntVar() + self.var2.set(1) + Checkbutton(self.master, text="OpenLDAP", variable=self.var2, command=self.check2).grid(row=3, column=1, stick=tk.W, pady=4) - if is_fieldvalue_empty: - msgbox("Lütfen zorunlu alanları giriniz.", ok_button="Tamam") - return print('Z'); - if host =='': - print(field_values[0],field_values[1],field_values[2]) - else: - print(field_values[0], field_values[1]) + self.e2.grid(row=1, column=1) + self.e3.grid(row=2, column=1) + + tk.Button(self.master, text='Çıkış', command=self.master.quit).grid(row=4, column=0, sticky=tk.W, pady=4) + tk.Button(self.master, text='Tamam', command=self.show).grid(row=4, column=1, sticky=tk.W, pady=4) + tk.mainloop() + + def show(self): + + if self.var2.get() == 1: + if self.host != "": + print(self.e2.get()+" "+self.e3.get()+" "+"LDAP") + else: + print(self.e1.get()+" "+self.e2.get()+" "+self.e3.get()+" "+"LDAP") + + if self.var1.get() == 1: + if self.host != "": + print(self.e2.get()+" "+self.e3.get()+" "+"AD") + else: + print(self.e1.get()+" "+self.e2.get()+" "+self.e3.get()+" "+"AD") + + self.master.quit() + + def check1(self): + self.var2.set(0) + + def check2(self): + self.var1.set(0) if __name__ == '__main__': if len(sys.argv) > 1: try: - message=sys.argv[1] - title=sys.argv[2] - host=sys.argv[3] - ask(message,title, host) + m_message = sys.argv[1] + t_title = sys.argv[2] + h_host = sys.argv[3] + display = sys.argv[4] + os.environ["DISPLAY"] = display + app = AskRegister(m_message, t_title, h_host) except Exception as e: print(str(e)) else: - print('Argument fault. Check your parameters or content of parameters. Parameters: ' + str(sys.argv)) \ No newline at end of file + print("Argument fault. Check your parameters or content of parameters. Parameters:" + str(sys.argv)) \ No newline at end of file diff --git a/src/base/agreement/confirm.py b/src/base/agreement/confirm.py index 12d8847..1e2cc93 100644 --- a/src/base/agreement/confirm.py +++ b/src/base/agreement/confirm.py @@ -2,10 +2,10 @@ # -*- coding: utf-8 -*- # Author: Volkan Şahin +import os import sys import easygui - def confirm(message, title): choice = easygui.buttonbox(msg=message, title=title, choices=["Tamam"]) @@ -17,8 +17,10 @@ def confirm(message, title): if __name__ == '__main__': - if len(sys.argv) == 3: + if len(sys.argv) == 4: try: + display=sys.argv[3] + os.environ["DISPLAY"] = display confirm(sys.argv[1], sys.argv[2]) except Exception as e: print(str(e)) diff --git a/src/base/agreement/unregistrationmessage.py b/src/base/agreement/unregistrationmessage.py index 53d153b..25f1f06 100644 --- a/src/base/agreement/unregistrationmessage.py +++ b/src/base/agreement/unregistrationmessage.py @@ -1,3 +1,4 @@ +import os import sys from easygui import multpasswordbox, msgbox @@ -12,17 +13,17 @@ def ask(message, title): title=title, fields=(field_names)) if field_values is None: - return print('N'); + return print('N') - is_fieldvalue_empty = False; + is_fieldvalue_empty = False for value in field_values: if value == '': - is_fieldvalue_empty = True; + is_fieldvalue_empty = True if is_fieldvalue_empty: msgbox("Lütfen zorunlu alanları giriniz.", ok_button="Tamam") - return print('Z'); + return print('Z') print(field_values[0], field_values[1]) @@ -32,6 +33,8 @@ if __name__ == '__main__': try: message=sys.argv[1] title=sys.argv[2] + display = sys.argv[3] + os.environ["DISPLAY"] = display ask(message,title) except Exception as e: print(str(e)) diff --git a/src/base/command/command_runner.py b/src/base/command/command_runner.py index 2cfd080..3b817c2 100644 --- a/src/base/command/command_runner.py +++ b/src/base/command/command_runner.py @@ -13,6 +13,7 @@ from base.system.system import System from base.timer.setup_timer import SetupTimer from base.timer.timer import Timer from base.util.util import Util +from base.default_policy.default_policy import DefaultPolicy class CommandRunner(object): @@ -25,6 +26,7 @@ class CommandRunner(object): self.conf_manager = scope.get_configuration_manager() self.db_service = scope.get_db_service() self.execute_manager = scope.get_execution_manager() + self.default_policy = DefaultPolicy() def check_last_login(self): last_login_tmstmp = self.db_service.select_one_result('session', 'timestamp') @@ -36,6 +38,16 @@ class CommandRunner(object): else: return True + def delete_polkit_user(self): + content = "[Configuration] \nAdminIdentities=unix-user:root" + ahenk_policy_file = "/etc/polkit-1/localauthority.conf.d/99-ahenk-policy.conf" + if not Util.is_exist(ahenk_policy_file): + self.logger.info('Ahenk polkit file not found') + else: + Util.delete_file(ahenk_policy_file) + Util.write_file(ahenk_policy_file, content) + self.logger.info('Root added ahenk polkit file') + def run_command_from_fifo(self, num, stack): """ docstring""" @@ -61,18 +73,24 @@ class CommandRunner(object): display = json_data['display'] desktop = json_data['desktop'] - ip = None if 'ip' in json_data: ip = json_data['ip'] self.logger.info('login event is handled for user: {0}'.format(username)) + Util.execute("systemctl restart sssd.service") login_message = self.message_manager.login_msg(username,ip) self.messenger.send_direct_message(login_message) agreement = Agreement() agreement_choice = None + ## Default policy for users + + self.logger.info("Applying default policies for user {0}".format(username)) + self.default_policy.default_firefox_policy(username) + self.default_policy.disable_update_package_notify(username) + if agreement.check_agreement(username) is not True and System.Ahenk.agreement() == '1': self.logger.debug('User {0} has not accepted agreement.'.format(username)) thread_ask = Process(target=agreement.ask, args=(username, display,)) @@ -151,6 +169,9 @@ class CommandRunner(object): logout_message = self.message_manager.logout_msg(username,ip) self.messenger.send_direct_message(logout_message) + self.logger.info('Ahenk polkit file deleting..') + self.delete_polkit_user() + self.plugin_manager.process_mode('logout', username) self.plugin_manager.process_mode('safe', username) @@ -160,7 +181,6 @@ class CommandRunner(object): message = json.dumps(json_data['message']) self.messenger.send_direct_message(message) - elif str(json_data['event']) == 'unregister': self.logger.info('Unregistering..') unregister_message = self.message_manager.unregister_msg() diff --git a/src/base/database/ahenk_db_service.py b/src/base/database/ahenk_db_service.py index c3a78fc..b78e85b 100644 --- a/src/base/database/ahenk_db_service.py +++ b/src/base/database/ahenk_db_service.py @@ -55,6 +55,7 @@ class AhenkDbService(object): self.check_and_create_table('mail', ['id INTEGER PRIMARY KEY AUTOINCREMENT', 'command TEXT', 'mailstatus INTEGER', 'timestamp TEXT']) self.check_and_create_table('service', ['id INTEGER PRIMARY KEY AUTOINCREMENT', 'serviceName TEXT', 'serviceStatus TEXT','timestamp TEXT','task_id INTEGER']) + self.check_and_create_table('app_restriction', ['id INTEGER PRIMARY KEY AUTOINCREMENT', 'application_name TEXT', 'username TEXT', 'restriction INTEGER']) def get_cols(self, table_name): @@ -193,4 +194,4 @@ class AhenkDbService(object): self.cursor.close() self.connection.close() except Exception as e: - self.logger.error('Closing database connection error: {0}'.format(str(e))) \ No newline at end of file + self.logger.error('Closing database connection error: {0}'.format(str(e))) diff --git a/src/base/default_policy/__init__.py b/src/base/default_policy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/base/default_policy/config-files/xfce4-notifyd.xml b/src/base/default_policy/config-files/xfce4-notifyd.xml new file mode 100644 index 0000000..37f91dd --- /dev/null +++ b/src/base/default_policy/config-files/xfce4-notifyd.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/base/default_policy/default_policy.py b/src/base/default_policy/default_policy.py new file mode 100644 index 0000000..7b8c345 --- /dev/null +++ b/src/base/default_policy/default_policy.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Tuncay Çolak +# Author: Hasan Kara + +# Default Policy for users + +from base.scope import Scope +from base.util.util import Util +import xml.etree.ElementTree as ET + + +class DefaultPolicy: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.util = Util() + + ## default firefox policy for user + def default_firefox_policy(self, username): + exec_command = None + firefox_path = None + + if self.util.is_exist("/usr/lib/firefox-esr/"): + firefox_path = "/usr/lib/firefox-esr/" + exec_command = "firefox-esr" + + elif self.util.is_exist('/opt/firefox-esr/'): + firefox_path = "/opt/firefox-esr/" + exec_command = "firefox-esr" + + elif self.util.is_exist('/usr/lib/iceweasel/'): + firefox_path = "/usr/lib/iceweasel/" + exec_command = "iceweasel" + + elif self.util.is_exist('/opt/firefox/'): + firefox_path = "/opt/firefox/" + exec_command = "firefox" + + else: + self.logger.error('Firefox installation path not found') + + self.logger.info("if mozilla profile is not created run firefox to create profile for user: " + username) + if not Util.is_exist("/home/" + username + "/.mozilla/"): + self.logger.info("firefox profile does not exist. Check autostart file.") + if not Util.is_exist("/home/" + username + "/.config/autostart/"): + self.logger.info(".config/autostart folder does not exist. Creating folder.") + Util.create_directory("/home/" + username + "/.config/autostart/") + else: + self.logger.info(".config/autostart folder exists.") + self.logger.info( + "Checking if {0}-autostart-for-profile.desktop autorun file exists.".format(exec_command)) + + if not Util.is_exist( + "/home/" + username + "/.config/autostart/{0}-autostart-for-profile.desktop".format(exec_command)): + self.logger.info( + "{0}-autostart-for-profile.desktop autorun file does not exists. Creating file.".format( + exec_command)) + Util.create_file( + "/home/" + username + "/.config/autostart/{0}-autostart-for-profile.desktop".format(exec_command)) + content = "[Desktop Entry]\n\n" \ + "Type=Application\n\n" \ + "Exec={0}{1} www.liderahenk.org".format(firefox_path, exec_command) + Util.write_file( + "/home/" + username + "/.config/autostart/{0}-autostart-for-profile.desktop".format(exec_command), + content) + self.logger.info( + "Autorun config is written to {0}-autostart-for-profile.desktop.".format(exec_command)) + else: + self.logger.info("{0}-autostart-for-profile.desktop exists".format(exec_command)) + else: + self.logger.info(".mozilla firefox profile path exists. Delete autorun file.") + Util.delete_file( + "/home/" + username + "/.config/autostart/{0}-autostart-for-profile.desktop".format(exec_command)) + + + ## disabled update package notify for user + def disable_update_package_notify(self, username): + + xfce4_notify_template_path = "/usr/share/ahenk/base/default_policy/config-files/xfce4-notifyd.xml" + + fileName = "/home/{0}/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-notifyd.xml".format(username) + + if not self.util.is_exist(fileName): + ## if configuration file does not exist will be create /home/{username}/.config/xfce4/xfconf/xfce-perchannel-xml/ + self.logger.info("Configuration file does not exist") + self.util.create_directory("/home/{0}/.config/xfce4/xfconf/xfce-perchannel-xml/".format(username)) + self.logger.info("Created directory /home/{0}/.config/xfce4/xfconf/xfce-perchannel-xml/".format(username)) + self.util.copy_file(xfce4_notify_template_path, "/home/{0}/.config/xfce4/xfconf/xfce-perchannel-xml/".format(username)) + self.logger.info("Copy xfce4-notifyd.xml template file") + gid = self.util.file_group("/home/{0}".format(username)) + cmd = "chown -R {0}:{1} /home/{0}/.config".format(username, gid) + self.util.execute(cmd) + self.logger.info("Set permissons for /home/{0}.config directory".format(username)) + + self.notifyd_xml_parser(username) + else: + self.logger.info("Configuration file exist") + self.notifyd_xml_parser(username) + + pk_update_icon_file = "/etc/xdg/autostart/pk-update-icon.desktop" + if self.util.is_exist(pk_update_icon_file): + self.logger.info("{0} file exists".format(pk_update_icon_file)) + self.util.rename_file(pk_update_icon_file, pk_update_icon_file+".ahenk") + self.logger.info("Renamed from {0} to {0}.ahenk".format(pk_update_icon_file)) + self.logger.info("Disabled autostart for pk-update-icon") + + else: + self.logger.info("File not found") + + self.logger.info("Disable notifications if there is a package update notification for user: " + username) + + def notifyd_xml_parser(self, username): + + fileName = "/home/{0}/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-notifyd.xml".format(username) + tree = ET.parse(fileName) + root = tree.getroot() + app_name_for_blocking = "pk-update-icon" + element = root.find("./property/[@name='applications']") + if element is None: + self.logger.info("applications element could not be found.") + else: + element = root.find("./property/property[@name='muted_applications']") + if element is None: + self.logger.info("muted_applications element could not be found.") + self.logger.info("adding muted_applications element to applications tag.") + element = root.find("./property/[@name='applications']") + new_element = ET.SubElement(element, 'property') + new_element.attrib["name"] = 'muted_applications' + new_element.attrib["type"] = 'array' + tree.write(fileName) + else: + self.logger.info("muted_applications tag exists.") + + self.logger.info("checking if '" + app_name_for_blocking + "' exists in muted_applications tag.") + element = root.find( + "./property/property[@name='muted_applications']/value[@value='{0}']".format(app_name_for_blocking)) + if element is None: + self.logger.info("'" + app_name_for_blocking + "' is not found in muted_applications element.") + self.logger.info("'" + app_name_for_blocking + "' will be added to muted_applications tag.") + element = root.find("./property/property[@name='muted_applications']") + new_element = ET.SubElement(element, 'value') + new_element.attrib["type"] = 'string' + new_element.attrib["value"] = app_name_for_blocking + tree.write(fileName) + else: + self.logger.info("'" + app_name_for_blocking + "' is already added to muted_applications tag.") \ No newline at end of file diff --git a/src/base/execution/execution_manager.py b/src/base/execution/execution_manager.py index 8a17089..7547ca5 100644 --- a/src/base/execution/execution_manager.py +++ b/src/base/execution/execution_manager.py @@ -45,6 +45,7 @@ class ExecutionManager(object): self.event_manager.register_event(MessageType.RESPONSE_AGREEMENT.value, self.agreement_update) self.event_manager.register_event(MessageType.UPDATE_SCHEDULED_TASK.value, self.update_scheduled_task) self.event_manager.register_event(MessageType.REGISTRATION_RESPONSE.value, self.unregister) # registration message for unregister event + self.event_manager.register_event(MessageType.LOGIN_RESPONSE.value, self.login_response) # registration message for unregister event def agreement_update(self, arg): @@ -410,9 +411,9 @@ class ExecutionManager(object): self.logger.info('Registration is failed. User not authorized') Util.show_message(user_name,display,'Ahenk Lider MYS sisteminden çıkarmak için yetkili kullanıcı haklarına sahip olmanız gerekmektedir.', 'Kullanıcı Yetkilendirme Hatası') - else : + else: Util.show_message(user_name, display, "Ahenk Lider MYS sisteminden çıkarılmıştır.", "") - if Util.show_message(user_name, display, "Değişikliklerin etkili olması için sistemi yeniden başlatmanız gerekmektedir.", "") : + if Util.show_message(user_name, display, "Değişikliklerin etkili olması için sistem yeniden başlatılacaktır. Lütfen bekleyiniz...", "") : registration= Scope.get_instance().get_registration() registration.purge_and_unregister() @@ -531,3 +532,23 @@ class ExecutionManager(object): user_execution_id=json_data['userCommandExecutionId'], agent_expiration_date=json_data['agentPolicyExpirationDate'], user_expiration_date=json_data['userPolicyExpirationDate']) + + def login_response(self, msg): + jData = json.loads(msg) + username = jData['userName'] + if username is not None: + self.create_sudo_polkit(username) + + + def create_sudo_polkit(self,username): + content = "[Configuration] \nAdminIdentities=unix-user:{}".format(username) + ahenk_policy_file = "/etc/polkit-1/localauthority.conf.d/99-ahenk-policy.conf" + if not Util.is_exist(ahenk_policy_file): + Util.create_file(ahenk_policy_file) + Util.write_file(ahenk_policy_file, content) + self.logger.debug('Ahenk polkit file created and user added.. User : {}'.format(username)) + else: + self.logger.debug('Writing result to file') + Util.delete_file(ahenk_policy_file) + Util.create_file(ahenk_policy_file) + Util.write_file(ahenk_policy_file, content) diff --git a/src/base/messaging/anonymous_messenger.py b/src/base/messaging/anonymous_messenger.py index e2012da..62be497 100644 --- a/src/base/messaging/anonymous_messenger.py +++ b/src/base/messaging/anonymous_messenger.py @@ -95,8 +95,8 @@ class AnonymousMessenger(ClientXMPP): def recv_direct_message(self, msg): if msg['type'] in ['normal']: - self.logger.debug('---------->Received message: {0}'.format(str(msg['body']))) - self.logger.debug('Reading registration reply') + self.logger.info('---------->Received message: {0}'.format(str(msg['body']))) + self.logger.info('Reading registration reply') j = json.loads(str(msg['body'])) message_type = j['type'] status = str(j['status']).lower() diff --git a/src/base/messaging/messaging.py b/src/base/messaging/messaging.py index e6e4c13..6aff7ac 100644 --- a/src/base/messaging/messaging.py +++ b/src/base/messaging/messaging.py @@ -72,6 +72,7 @@ class Messaging(object): data['ipAddresses'] = str(System.Hardware.Network.ip_addresses()).replace('[', '').replace(']', '') data['timestamp'] = Util.timestamp() data['userIp'] = ip + data['hostname'] = str(System.Os.hostname()) self.logger.debug('USER IP : '+ str(ip)+ ' IPADDRESSES : '+ str(System.Hardware.Network.ip_addresses()).replace('[', '').replace(']', '')) @@ -113,7 +114,7 @@ class Messaging(object): self.logger.debug('Get Policies message was created') return json_data - def registration_msg(self, userName= None, userPassword=None): + def registration_msg(self, userName= None, userPassword=None, directoryServer=None): data = dict() data['type'] = 'REGISTER' data['from'] = self.db_service.select_one_result('registration', 'jid', ' 1=1') @@ -132,9 +133,13 @@ class Messaging(object): if userPassword is not None: data["userPassword"] = str(userPassword) + if directoryServer is not None: + data["directoryServer"] = str(directoryServer) + data['timestamp'] = self.db_service.select_one_result('registration', 'timestamp', ' 1=1') json_data = json.dumps(data) self.logger.debug('Registration message was created') + self.logger.info('Registration message was created. Data content: '+ json_data) return json_data def ldap_registration_msg(self): diff --git a/src/base/messaging/messenger.py b/src/base/messaging/messenger.py index 161b647..5a6428f 100644 --- a/src/base/messaging/messenger.py +++ b/src/base/messaging/messenger.py @@ -99,10 +99,24 @@ class Messenger(ClientXMPP): def recv_direct_message(self, msg): if msg['type'] in ['normal']: - self.logger.info('---------->Received message: {0}'.format(str(msg['body']))) try: j = json.loads(str(msg['body'])) message_type = j['type'] + self.logger.debug("Get message type: "+str(message_type)) + + if j['type'] == "EXECUTE_POLICY": + self.logger.info('---------->Received message: {0}'.format(str(msg['body']))) + + if j['type'] == "EXECUTE_TASK": + i = json.loads(str(j['task'])) + plugin_name = i['plugin']['name'] + if plugin_name == "manage-root": + parameter_map = i['parameterMap'] + parameter_map.pop("RootPassword") + self.logger.info("---------->Received message: {}".format(str(parameter_map))) + else: + self.logger.info('---------->Received message: {0}'.format(str(msg['body']))) + self.event_manger.fireEvent(message_type, str(msg['body'])) self.logger.debug('Fired event is: {0}'.format(message_type)) except Exception as e: diff --git a/src/base/model/enum/message_type.py b/src/base/model/enum/message_type.py index a677665..a67814c 100644 --- a/src/base/model/enum/message_type.py +++ b/src/base/model/enum/message_type.py @@ -24,3 +24,4 @@ class MessageType(Enum): RESPONSE_AGREEMENT = 'RESPONSE_AGREEMENT' UPDATE_SCHEDULED_TASK = 'UPDATE_SCHEDULED_TASK' REGISTRATION_RESPONSE ='REGISTRATION_RESPONSE' + LOGIN_RESPONSE = 'LOGIN_RESPONSE' diff --git a/src/base/registration/config-files/krb5.conf b/src/base/registration/config-files/krb5.conf new file mode 100644 index 0000000..65a33e9 --- /dev/null +++ b/src/base/registration/config-files/krb5.conf @@ -0,0 +1,37 @@ +[libdefaults] +default_realm = ENGEREK.LOCAL +kdc_timesync = 1 +ccache_type = 4 +forwardable = true +proxiable = true +fcc-mit-ticketflags = true + +[realms] +ENGEREK.LOCAL = { +admin_server = liderahenk.engerek.local +kdc = liderahenk.engerek.local +} + +[domain_realm] +.engerek.local = ENGEREK.LOCAL + + +------------------------------------------------------------------------------------------ +[libdefaults] +default_realm = ENGEREK.LOCAL +kdc_timesync = 1 +ccache_type = 4 +forwardable = true +proxiable = true +fcc-mit-ticketflags = true + +[realms] +###realm### = { + +###admin_server### +###kdc### + +} + +[domain_realm] +###own_domain_realm### \ No newline at end of file diff --git a/src/base/registration/config-files/ldap b/src/base/registration/config-files/ldap new file mode 100644 index 0000000..f3ec76b --- /dev/null +++ b/src/base/registration/config-files/ldap @@ -0,0 +1,20 @@ +Name: Ahenk LDAP Authentication +Default: yes +Priority: 128 +Auth-Type: Primary +Auth-Initial: + [success=end authinfo_unavail=ignore default=ignore] pam_ldap.so +Auth: + [success=end authinfo_unavail=ignore default=ignore] pam_ldap.so use_first_pass +Account-Type: Primary +Account: + [success=end new_authtok_reqd=done authinfo_unavail=1 default=ignore] pam_ldap.so +Password-Type: Primary +Password-Initial: + [success=end user_unknown=ignore default=die] pam_ldap.so +Password: + [success=end user_unknown=ignore default=die] pam_ldap.so try_first_pass +Session-Type: Additional +Session: + optional pam_ldap.so + required pam_mkhomedir.so skel=/etc/skel umask=066 \ No newline at end of file diff --git a/src/base/registration/config-files/pam_script b/src/base/registration/config-files/pam_script new file mode 100644 index 0000000..bd149bd --- /dev/null +++ b/src/base/registration/config-files/pam_script @@ -0,0 +1,12 @@ +Name: Ahenk PAM scripts +Default: yes +Priority: 257 +Auth-Type: Primary +Auth: + optional pam_script.so +Account-Type: Primary +Account: + optional pam_script.so +Session-Type: Additional +Session: + optional pam_script.so diff --git a/src/base/registration/config-files/sssd.conf b/src/base/registration/config-files/sssd.conf new file mode 100644 index 0000000..d974c8a --- /dev/null +++ b/src/base/registration/config-files/sssd.conf @@ -0,0 +1,44 @@ +[sssd] +config_file_version = 2 +services = nss, pam, sudo +domains = LDAP + +[nss] + +[sudo] + +[pam] +pam_verbosity=2 +pam_account_locked_message = Hesap Kilitli +offline_credentials_expiration = 90 + +[domain/LDAP] +debug_level = 9 +id_provider = ldap +auth_provider = ldap +access_provider = ldap +#ldap_access_filter = (employeeType=admin) +ldap_access_order = ppolicy +pam_verbosity=2 +###ldap_pwdlockout_dn### +ldap_schema = rfc2307 +###ldap_uri### +###ldap_default_bind_dn### +###ldap_default_authtok### +ldap_default_authtok_type = password +###ldap_search_base### +###ldap_user_search_base### +###ldap_group_search_base### +ldap_user_object_class = posixAccount +ldap_user_gecos = cn +ldap_tls_reqcert = never +ldap_auth_disable_tls_never_use_in_production = true +override_shell = /bin/bash +enumerate = true +cache_credentials = true +sudo_provider = ldap +###ldap_sudo_search_base### +###90 days +ldap_sudo_full_refresh_interval=7776000 +###30 days +ldap_sudo_smart_refresh_interval=2592000 \ No newline at end of file diff --git a/src/base/registration/config-files/sssd_ad.conf b/src/base/registration/config-files/sssd_ad.conf new file mode 100644 index 0000000..2ccbc47 --- /dev/null +++ b/src/base/registration/config-files/sssd_ad.conf @@ -0,0 +1,25 @@ +[nss] +filter_groups = root,adm +filter_users = root,adm +reconnection_retries = 3 + +[pam] +reconnection_retries = 3 + +[sssd] +###domains### +config_file_version = 2 +services = nss, pam + +###[domain/### +###ad_domain### +###krb5_realm### +realmd_tags = manages-system joined-with-adcli +cache_credentials = True +id_provider = ad +krb5_store_password_if_offline = True +default_shell = /bin/bash +ldap_id_mapping = True +use_fully_qualified_names = False +fallback_homedir = /home/%u@%d +access_provider = ad diff --git a/src/base/registration/execute_cancel_ldap_login.py b/src/base/registration/execute_cancel_ldap_login.py new file mode 100644 index 0000000..5b91b7e --- /dev/null +++ b/src/base/registration/execute_cancel_ldap_login.py @@ -0,0 +1,101 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +from base.scope import Scope +from base.util.util import Util +import re + + +class ExecuteCancelLDAPLogin: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.util = Util() + + def cancel(self): + self.logger.info('Purge ldap packages') + self.util.execute("apt-get install sudo -y") + self.util.execute("apt purge libpam-ldap libnss-ldap ldap-utils sudo-ldap nss-updatedb libnss-db libpam-ccreds -y") + self.util.execute("apt autoremove -y") + + self.logger.info('purging successfull') + + # pattern for clearing file data from spaces, tabs and newlines + pattern = re.compile(r'\s+') + + ldap_back_up_file_path = "/usr/share/ahenk/pam_scripts_original/ldap" + ldap_original_file_path = "/usr/share/pam-configs/ldap" + + pam_script_back_up_file_path = "/usr/share/ahenk/pam_scripts_original/pam_script" + pam_script_original_file_path = "/usr/share/pam-configs/pam_script" + + if self.util.is_exist(ldap_back_up_file_path): + self.logger.info("Replacing {0} with {1}".format(ldap_original_file_path, ldap_back_up_file_path)) + self.util.copy_file(ldap_back_up_file_path, ldap_original_file_path) + self.logger.info("Deleting {0}".format(ldap_back_up_file_path)) + self.util.delete_file(ldap_back_up_file_path) + + if self.util.is_exist(pam_script_back_up_file_path): + self.logger.info( + "Replacing {0} with {1}".format(pam_script_original_file_path, pam_script_back_up_file_path)) + self.util.copy_file(pam_script_back_up_file_path, pam_script_original_file_path) + self.logger.info("Deleting {0}".format(pam_script_back_up_file_path)) + self.util.delete_file(pam_script_back_up_file_path) + + (result_code, p_out, p_err) = self.util.execute("DEBIAN_FRONTEND=noninteractive pam-auth-update --package") + if result_code == 0: + self.logger.info("'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' has run successfully") + else: + self.logger.error( + "'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' could not run successfully: " + p_err) + + # Configure nsswitch.conf + file_ns_switch = open("/etc/nsswitch.conf", 'r') + file_data = file_ns_switch.read() + + # cleared file data from spaces, tabs and newlines + text = pattern.sub('', file_data) + + did_configuration_change = False + if "passwd:compatldap[NOTFOUND=return]db" in text: + file_data = file_data.replace("passwd: compat ldap [NOTFOUND=return] db", "passwd: compat") + did_configuration_change = True + + if "group:compatldap[NOTFOUND=return]db" in text: + file_data = file_data.replace("group: compat ldap [NOTFOUND=return] db", "group: compat") + did_configuration_change = True + + if "shadow:compatldap" in text: + file_data = file_data.replace("shadow: compat ldap", "shadow: compat") + did_configuration_change = True + + if "#gshadow:files" in text: + file_data = file_data.replace("#gshadow: files", "gshadow: files") + did_configuration_change = True + + if did_configuration_change: + self.logger.info("nsswitch.conf configuration has been configured") + else: + self.logger.info("nsswitch.conf has already been configured") + + file_ns_switch.close() + file_ns_switch = open("/etc/nsswitch.conf", 'w') + file_ns_switch.write(file_data) + file_ns_switch.close() + + # Configure ldap-cache + nss_update_cron_job_file_path = "/etc/cron.daily/nss-updatedb" + if self.util.is_exist(nss_update_cron_job_file_path): + self.util.delete_file(nss_update_cron_job_file_path) + self.logger.info("{0} is deleted.".format(nss_update_cron_job_file_path)) + + # Configure lightdm.service + pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" + if self.util.is_exist(pardus_xfce_path): + self.logger.info("99-pardus-xfce.conf exists. Deleting file.") + self.util.delete_file(pardus_xfce_path) + + self.util.execute("systemctl restart nscd.service") + self.logger.info("Operation finished") + diff --git a/src/base/registration/execute_cancel_sssd_ad_authentication.py b/src/base/registration/execute_cancel_sssd_ad_authentication.py new file mode 100644 index 0000000..6a37d05 --- /dev/null +++ b/src/base/registration/execute_cancel_sssd_ad_authentication.py @@ -0,0 +1,126 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Agah Hulusi ÖZ + +from base.scope import Scope +from base.util.util import Util +import re + +class ExecuteCancelSSSDAdAuthentication: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.util = Util() + self.ad_info_path = "/etc/ahenk/ad_info" + + def cancel(self): + try: + # Deleting packages require for AD entegration + self.util.execute( + "apt purge realmd sssd sssd-tools adcli krb5-user packagekit samba-common samba-common-bin samba-libs -y") + self.util.execute("apt autoremove -y") + + # Read information about AD + if self.util.is_exist(self.ad_info_path): + file_data = self.util.read_file_by_line(self.ad_info_path) + self.ip_address = file_data[0].strip("\n") + self.host_name = file_data[1].strip("\n") + self.logger.info(self.ip_address) + self.logger.info(self.host_name) + self.logger.info("Information read successfully from ad_info.") + else: + self.logger.error("ad_info file not found") + + if self.util.is_exist("/etc/sssd"): + # self.util.delete_folder("/etc/sssd") + self.logger.info("SSSD is deleted") + else: + self.logger.info("SSSD is not exist") + + # Re-Configure dhclient.conf deleting AD IP address + dhclient_conf_path = "/etc/dhcp/dhclient.conf" + file_dhclient = open(dhclient_conf_path, 'r') + file_data = file_dhclient.read() + + if "prepend domain-name-servers {};".format(self.ip_address) in file_data: + file_data = file_data.replace(("prepend domain-name-servers {};".format(self.ip_address)), + "#prepend domain-name-servers 127.0.0.1;") + self.logger.info("dhclient is reconfigured") + else: + self.logger.error("dhclient is'not reconfigured") + + file_dhclient.close() + file_dhclient = open(dhclient_conf_path, 'w') + file_dhclient.write(file_data) + file_dhclient.close() + + # Configure hosts for deleting AD "IP address" and "AD hostname" + hosts_conf_path = "/etc/hosts" + file_hosts = open(hosts_conf_path, 'r') + file_data = file_hosts.read() + + if ("{0} {1}".format(self.ip_address, self.host_name)) in file_data: + file_data = file_data.replace(("{0} {1}".format(self.ip_address, self.host_name)), " ") + self.logger.info("hosts is configured") + else: + self.logger.error("hosts is not configured") + file_hosts.close() + file_hosts = open(hosts_conf_path, 'w') + file_hosts.write(file_data) + file_hosts.close() + + # Configure common-session for deleting home directories for AD users + common_session_conf_path = "/etc/pam.d/common-session" + file_common_session = open(common_session_conf_path, 'r') + file_data = file_common_session.read() + + if "session optional pam_mkhomedir.so skel=/etc/skel umask=077" in file_data: + file_data = file_data.replace("session optional pam_mkhomedir.so skel=/etc/skel umask=077", " ") + self.logger.info("common-session is configured") + else: + self.logger.error("common session is not configured") + + file_common_session.close() + file_common_session = open(common_session_conf_path, 'w') + file_common_session.write(file_data) + file_common_session.close() + + # Configure resolv.conf for deleting AD IP address + resolv_conf_path = "/etc/resolv.conf" + file_resolv = open(resolv_conf_path, 'r') + file_data = file_resolv.read() + + if ("nameserver {0}".format(self.ip_address)) in file_data: + file_data = file_data.replace(("nameserver {0}".format(self.ip_address)), "") + self.logger.info("resolv.conf is configured") + else: + self.logger.error("resolv is not configured") + + file_resolv.close() + file_resolv = open(resolv_conf_path, 'w') + file_resolv.write(file_data) + file_resolv.close() + + # Deleting ad_info file + if self.util.is_exist(self.ad_info_path): + self.util.delete_file(self.ad_info_path) + self.logger.info("Deleted ad_info file") + else: + self.logger.error("ad_info file not found") + + # Configure lightdm.service + pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" + if self.util.is_exist(pardus_xfce_path): + self.logger.info("99-pardus-xfce.conf exists. Deleting file.") + self.util.delete_file(pardus_xfce_path) + self.util.execute("systemctl restart nscd.service") + else: + self.logger.info("99-pardus-xfce.conf not found") + + self.logger.info("AD Login iptal etme işlemi başarı ile sağlandı.") + return True + + except Exception as e: + self.logger.error(str(e)) + self.logger.info("AD Login İptal etme işlemi esnasında hata oluştu.") + return False diff --git a/src/base/registration/execute_cancel_sssd_authentication.py b/src/base/registration/execute_cancel_sssd_authentication.py new file mode 100644 index 0000000..a19823d --- /dev/null +++ b/src/base/registration/execute_cancel_sssd_authentication.py @@ -0,0 +1,85 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +from base.scope import Scope +from base.util.util import Util +import re + + +class ExecuteCancelSSSDAuthentication: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.util = Util() + + def cancel(self): + self.util.execute("apt purge libpam-sss sssd-common -y") + self.util.execute("apt autoremove -y") + + if self.util.is_exist("/etc/sssd"): + self.util.delete_folder("/etc/sssd") + + # pattern for clearing file data from spaces, tabs and newlines + pattern = re.compile(r'\s+') + + # Configure nsswitch.conf + # file_ns_switch = open("/etc/nsswitch.conf", 'r') + # file_data = file_ns_switch.read() + # + # # cleared file data from spaces, tabs and newlines + # text = pattern.sub('', file_data) + + # did_configuration_change = False + # if "passwd:compatsss" in text: + # file_data = file_data.replace("passwd: compat sss", "passwd: compat") + # did_configuration_change = True + # + # if "group:compatsss" in text: + # file_data = file_data.replace("group: compat sss", "group: compat") + # did_configuration_change = True + # + # if "shadow:compatsss" in text: + # file_data = file_data.replace("shadow: compat sss", "shadow: compat") + # did_configuration_change = True + # + # if "services:dbfilessss" in text: + # file_data = file_data.replace("services: db files sss", "services: db files") + # did_configuration_change = True + # + # if "netgroup:nissss" in text: + # file_data = file_data.replace("netgroup: nis sss", "netgroup: nis") + # did_configuration_change = True + # + # if "sudoers:filessss" in text: + # file_data = file_data.replace("sudoers: files sss", "") + # did_configuration_change = True + # + # if did_configuration_change: + # self.logger.info("nsswitch.conf configuration has been configured") + # else: + # self.logger.info("nsswitch.conf has already been configured") + + # file_ns_switch.close() + # file_ns_switch = open("/etc/nsswitch.conf", 'w') + # file_ns_switch.write(file_data) + # file_ns_switch.close() + + common_session_conf_path = "/etc/pam.d/common-session" + + # configure common-session for creating home directories for ldap users + file_common_session = open(common_session_conf_path, 'r') + file_data = file_common_session.read() + + if "session optional pam_mkhomedir.so skel=/etc/skel umask=077" in file_data: + file_data = file_data.replace("session optional pam_mkhomedir.so skel=/etc/skel umask=077", "") + self.logger.info("common-session is configured") + + file_common_session.close() + file_common_session = open(common_session_conf_path, 'w') + file_common_session.write(file_data) + file_common_session.close() + + self.util.execute("systemctl restart nscd.service") + self.logger.info("LDAP Login iptal etme işlemi başarı ile sağlandı.") + diff --git a/src/base/registration/execute_ldap_login.py b/src/base/registration/execute_ldap_login.py new file mode 100644 index 0000000..2e189fe --- /dev/null +++ b/src/base/registration/execute_ldap_login.py @@ -0,0 +1,232 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +from base.scope import Scope +from base.util.util import Util +import re + + +class ExecuteLDAPLogin: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.util = Util() + + def login(self, server_address, dn, version, admin_dn, admin_password): + try: + self.logger.info("----------------> server_address: " + server_address) + self.logger.info("----------------> dn: " + dn) + self.logger.info("----------------> version: " + version) + self.logger.info("----------------> admin_dn: " + admin_dn) + self.logger.info("----------------> admin_password: " + admin_password) + #(result_code, p_out, p_err) = self.util.execute("/bin/bash /usr/share/ahenk/base/registration/scripts/test.sh") + (result_code, p_out, p_err) = self.util.execute("/bin/bash /usr/share/ahenk/base/registration/scripts/ldap-login.sh {0} {1} {2} {3} {4}".format(server_address, "\'" + dn + "\'", "\'" + admin_dn + "\'", "\'" + admin_password + "\'", version)) + if result_code == 0: + self.logger.info("Script has run successfully") + else: + self.logger.error("Script could not run successfully: " + p_err) + + # pattern for clearing file data from spaces, tabs and newlines + pattern = re.compile(r'\s+') + + pam_scripts_original_directory_path = "/usr/share/ahenk/pam_scripts_original" + + ldap_back_up_file_path = "/usr/share/ahenk/pam_scripts_original/ldap" + ldap_original_file_path = "/usr/share/pam-configs/ldap" + ldap_configured_file_path = "/usr/share/ahenk/plugins/ldap-login/config-files/ldap" + + pam_script_back_up_file_path = "/usr/share/ahenk/pam_scripts_original/pam_script" + pam_script_original_file_path = "/usr/share/pam-configs/pam_script" + pam_script_configured_file_path = "/usr/share/ahenk/plugins/ldap-login/config-files/pam_script" + + # create pam_scripts_original directory if not exists + if not self.util.is_exist(pam_scripts_original_directory_path): + self.logger.info("Creating {0} directory.".format(pam_scripts_original_directory_path)) + self.util.create_directory(pam_scripts_original_directory_path) + + if self.util.is_exist(ldap_back_up_file_path): + self.logger.info("Changing {0} with {1}.".format(ldap_original_file_path, ldap_configured_file_path)) + self.util.copy_file(ldap_configured_file_path, ldap_original_file_path) + else: + self.logger.info("Backing up {0}".format(ldap_original_file_path)) + self.util.copy_file(ldap_original_file_path, ldap_back_up_file_path) + self.logger.info( + "{0} file is replaced with {1}.".format(ldap_original_file_path, ldap_configured_file_path)) + self.util.copy_file(ldap_configured_file_path, ldap_original_file_path) + + if self.util.is_exist(pam_script_back_up_file_path): + self.util.copy_file(pam_script_configured_file_path, pam_script_original_file_path) + self.logger.info( + "{0} is replaced with {1}.".format(pam_script_original_file_path, pam_script_configured_file_path)) + else: + self.logger.info("Backing up {0}".format(pam_script_original_file_path)) + self.util.copy_file(pam_script_original_file_path, pam_script_back_up_file_path) + self.logger.info("{0} file is replaced with {1}".format(pam_script_original_file_path, + pam_script_configured_file_path)) + self.util.copy_file(pam_script_configured_file_path, pam_script_original_file_path) + + (result_code, p_out, p_err) = self.util.execute("DEBIAN_FRONTEND=noninteractive pam-auth-update --package") + if result_code == 0: + self.logger.info("'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' has run successfully") + else: + self.logger.error( + "'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' could not run successfully: " + p_err) + + # Configure nsswitch.conf + file_ns_switch = open("/etc/nsswitch.conf", 'r') + file_data = file_ns_switch.read() + + # cleared file data from spaces, tabs and newlines + text = pattern.sub('', file_data) + + is_configuration_done_before = False + if ("passwd:compatldap" not in text): + file_data = file_data.replace("passwd: compat", "passwd: compat ldap") + is_configuration_done_before = True + + if ("group:compatldap" not in text): + file_data = file_data.replace("group: compat", "group: compat ldap") + is_configuration_done_before = True + + if ("shadow:compatldap" not in text): + file_data = file_data.replace("shadow: compat", "shadow: compat ldap") + is_configuration_done_before = True + + if is_configuration_done_before: + self.logger.info("nsswitch.conf configuration has been completed") + else: + self.logger.info("nsswitch.conf is already configured") + + file_ns_switch.close() + file_ns_switch = open("/etc/nsswitch.conf", 'w') + file_ns_switch.write(file_data) + file_ns_switch.close() + + # configure ldap-cache + self.logger.info("Starting to ldap-cache configurations.") + result_code, p_out, p_err = self.util.execute("apt-get install nss-updatedb -y") + if result_code != 0: + self.logger.error("Error occured while downloading nss-updatedb.") + else: + self.logger.info("nss-updatedb downloaded successfully. Configuring /etc/nsswitch.conf.") + file_ns_switch = open("/etc/nsswitch.conf", 'r') + file_data = file_ns_switch.read() + + # cleared file data from spaces, tabs and newlines + text = pattern.sub('', file_data) + + did_configuration_change = False + if "passwd:compatldap[NOTFOUND=return]db" not in text: + file_data = file_data.replace("passwd: compat ldap", + "passwd: compat ldap [NOTFOUND=return] db") + did_configuration_change = True + + if "group:compatldap[NOTFOUND=return]db" not in text: + file_data = file_data.replace("group: compat ldap", + "group: compat ldap [NOTFOUND=return] db") + did_configuration_change = True + + if "gshadow:files" in text and "#gshadow:files" not in text: + file_data = file_data.replace("gshadow: files", "#gshadow: files") + did_configuration_change = True + + if did_configuration_change: + self.logger.info("nsswitch.conf configuration has been configured for ldap cache.") + else: + self.logger.info("nsswitch.conf has already been configured for ldap cache.") + + file_ns_switch.close() + file_ns_switch = open("/etc/nsswitch.conf", 'w') + file_ns_switch.write(file_data) + file_ns_switch.close() + self.util.execute("nss_updatedb ldap") + + # create cron job for ldap cache + content = "#!/bin/bash\n" \ + "nss-updatedb ldap" + nss_update_cron_job_file_path = "/etc/cron.daily/nss-updatedb" + if self.util.is_exist(nss_update_cron_job_file_path): + self.logger.info( + "{0} exists. File will be deleted and creating new one.".format(nss_update_cron_job_file_path)) + self.util.delete_file(nss_update_cron_job_file_path) + self.util.create_file(nss_update_cron_job_file_path) + self.util.write_file(nss_update_cron_job_file_path, content, 'w+') + self.util.execute("chmod +x " + nss_update_cron_job_file_path) + else: + self.logger.info( + "{0} doesnt exist. File will be created and content will be written.".format( + nss_update_cron_job_file_path)) + self.util.create_file(nss_update_cron_job_file_path) + self.util.write_file(nss_update_cron_job_file_path, content, 'w+') + self.util.execute("chmod +x " + nss_update_cron_job_file_path) + + # configure /etc/libnss-ldap.conf + libnss_ldap_file_path = "/etc/libnss-ldap.conf" + content = "bind_policy hard" \ + "\nnss_reconnect_tries 1" \ + "\nnss_reconnect_sleeptime 1" \ + "\nnss_reconnect_maxsleeptime 8" \ + "\nnss_reconnect_maxconntries 2" + if self.util.is_exist(libnss_ldap_file_path): + self.logger.info("{0} exists.".format(libnss_ldap_file_path)) + self.util.execute("sed -i '/bind_policy hard/c\\' " + libnss_ldap_file_path) + self.util.execute("sed -i '/nss_reconnect_tries 1/c\\' " + libnss_ldap_file_path) + self.util.execute("sed -i '/nss_reconnect_sleeptime 1/c\\' " + libnss_ldap_file_path) + self.util.execute("sed -i '/nss_reconnect_maxsleeptime 8/c\\' " + libnss_ldap_file_path) + self.util.execute("sed -i '/nss_reconnect_maxconntries 2/c\\' " + libnss_ldap_file_path) + self.util.write_file(libnss_ldap_file_path, content, 'a+') + self.logger.info("Configuration has been made to {0}.".format(libnss_ldap_file_path)) + + result_code, p_out, p_err = self.util.execute("apt-get install libnss-db libpam-ccreds -y") + if result_code != 0: + self.logger.error("Error occured while downloading libnss-db libpam-ccreds.") + else: + self.logger.error("libnss-db libpam-ccreds are downloaded.") + + # configure sudo-ldap + sudo_ldap_conf_file_path = "/etc/sudo-ldap.conf" + content = "sudoers_base ou=Roles," + dn \ + + "\nBASE " + dn \ + + "\nURI ldap://" + server_address + # clean if config is already written + self.util.execute("sed -i '/BASE /c\\' " + sudo_ldap_conf_file_path) + self.util.execute("sed -i '/sudoers_base /c\\' " + sudo_ldap_conf_file_path) + self.util.execute("sed -i '/URI /c\\' " + sudo_ldap_conf_file_path) + + if self.util.is_exist(sudo_ldap_conf_file_path): + self.logger.info("{0} exists.".format(sudo_ldap_conf_file_path)) + self.util.write_file(sudo_ldap_conf_file_path, content, 'a+') + self.logger.info("Content is written to {0} successfully.".format(sudo_ldap_conf_file_path)) + + # Configure lightdm.service + # check if 99-pardus-xfce.conf exists if not create + pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" + if not self.util.is_exist(pardus_xfce_path): + self.logger.info("99-pardus-xfce.conf does not exist.") + self.util.create_file(pardus_xfce_path) + + file_lightdm = open(pardus_xfce_path, 'a') + file_lightdm.write("[Seat:*]\n") + file_lightdm.write("greeter-hide-users=true") + file_lightdm.close() + self.logger.info("lightdm has been configured.") + else: + self.logger.info("99-pardus-xfce.conf exists. Delete file and create new one.") + self.util.delete_file(pardus_xfce_path) + self.util.create_file(pardus_xfce_path) + + file_lightdm = open(pardus_xfce_path, 'a') + file_lightdm.write("[Seat:*]") + file_lightdm.write("greeter-hide-users=true") + file_lightdm.close() + self.logger.info("lightdm.conf has been configured.") + self.util.execute("systemctl restart nscd.service") + self.util.execute("pam-auth-update --force") + self.logger.info("LDAP Login operation has been completed.") + + self.logger.info("LDAP Login işlemi başarı ile sağlandı.") + except Exception as e: + self.logger.error(str(e)) + self.logger.info("LDAP Login işlemi esnasında hata oluştu.") + raise Exception('LDAP Ayarları yapılırken hata oluştu. Lütfen ağ bağlantınızı kontrol ediniz. Deponuzun güncel olduğundan emin olunuz.') \ No newline at end of file diff --git a/src/base/registration/execute_sssd_ad_authentication.py b/src/base/registration/execute_sssd_ad_authentication.py new file mode 100644 index 0000000..7cdf993 --- /dev/null +++ b/src/base/registration/execute_sssd_ad_authentication.py @@ -0,0 +1,208 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Agah Hulusi ÖZ + +from base.scope import Scope +from base.util.util import Util + +class ExecuteSSSDAdAuthentication: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.util = Util() + + def authenticate(self, domain_name, host_name, ip_address, password, ad_username): + try: + # Create and Configure ad_info file + (result_code, p_out, p_err) = self.util.create_file("/etc/ahenk/ad_info") + if (result_code == 0): + self.logger.info("AD INFO başarılı bir şekilde oluşturuldu") + # Configure ad_info for deregisteration info + default_ad_info_path = "/etc/ahenk/ad_info" + file_default_ad_info = open(default_ad_info_path, 'r') + file_data = file_default_ad_info.read() + + file_data = file_data + ("{}".format(ip_address)) + "\n" + ("{}".format(host_name)) + "\n" + ( + "{}".format(domain_name)) + "\n" + ("{}".format(ad_username)) + self.logger.info("/etc/ahenk/ad_info bilgiler girildi.") + file_default_ad_info.close() + file_default_ad_info = open(default_ad_info_path, 'w') + file_default_ad_info.write(file_data) + file_default_ad_info.close() + else: + self.logger.error("ad_info oluşturma komutu başarısız : " + str(p_err)) + + self.logger.info("Authenticate starting....") + # Configure /etc/dhcp/dhclient.conf + dhclient_conf_path = "/etc/dhcp/dhclient.conf" + dhc_conf = self.util.read_file_by_line(dhclient_conf_path, "r") + dhc_conf_temp = open(dhclient_conf_path, 'w') + + for lines in dhc_conf: + if (lines == "#prepend domain-name-servers 127.0.0.1;\n"): + lines = lines.replace(lines, ("prepend domain-name-servers {};\n".format(ip_address))) + dhc_conf_temp.write(lines) + dhc_conf_temp.close() + + file_default_dhcp = open(dhclient_conf_path, 'r') + file_data = file_default_dhcp.read() + + if ("prepend domain-name-servers {};\n".format(ip_address)) not in file_data: + file_data = file_data + "\n" + ("prepend domain-name-servers {};".format(ip_address)) + + file_default_dhcp.close() + file_default_dhcp = open(dhclient_conf_path, 'w') + file_default_dhcp.write(file_data) + file_default_dhcp.close() + + # Configure /etc/resolv.conf + resolve_conf_path = "/etc/resolv.conf" + resolve_conf = self.util.read_file_by_line(resolve_conf_path, "r") + resolve_conf_temp = open(resolve_conf_path, 'w') + + for lines in resolve_conf: + if (lines == ("nameserver {}\n".format(ip_address))): + continue + lines = lines.replace(lines, ("#" + lines)) + resolve_conf_temp.write(lines) + resolve_conf_temp.close() + + file_default_resolve = open(resolve_conf_path, 'r') + file_data = file_default_resolve.read() + + if ("nameserver {}\n".format(ip_address)) not in file_data: + file_data = file_data + "\n" + ("nameserver {}\n".format(ip_address)) + self.logger.info("/etc/resolv.conf is configured") + + file_default_resolve.close() + file_default_resolve = open(resolve_conf_path, 'w') + file_default_resolve.write(file_data) + file_default_resolve.close() + + # Configure /etc/hosts + host_path = "/etc/hosts" + file_default_hosts = open(host_path, 'r') + file_data = file_default_hosts.read() + + if ("{0} {1}".format(ip_address, host_name)) not in file_data: + file_data = file_data + "\n" + ("{0} {1}".format(ip_address, host_name)) + self.logger.info("/etc/hosts is configured") + + file_default_hosts.close() + file_default_hosts = open(host_path, 'w') + file_default_hosts.write(file_data) + file_default_hosts.close() + + # Execute the script that required for "samba-common" and "krb5" + (result_code, p_out, p_err) = self.util.execute("/bin/bash /usr/share/ahenk/base/registration/scripts/ad.sh {0} {1}".format(domain_name.upper(),host_name)) + + if(result_code == 0): + self.logger.info("Script başarılı bir şekilde çalıştırıldı.") + else: + self.logger.error("Script başarısız oldu : " + str(p_err)) + + # Installation of required packages + (result_code, p_out, p_err) = self.util.execute("sudo apt-get -y install realmd sssd sssd-tools adcli packagekit samba-common-bin samba-libs") + if (result_code == 0): + self.logger.info("İndirmeler Başarılı") + else: + self.logger.error("İndirmeler Başarısız : " + str(p_err)) + + # Configure pam.d/common-session + pamd_common_session_path = "/etc/pam.d/common-session" + file_default_pam = open(pamd_common_session_path, 'r') + file_data = file_default_pam.read() + + if "session optional pam_mkhomedir.so skel=/etc/skel umask=077" not in file_data: + file_data = file_data + "\n" + "session optional pam_mkhomedir.so skel=/etc/skel umask=077" + self.logger.info("/etc/pam.d/common-session is configured") + + file_default_pam.close() + file_default_pam = open(pamd_common_session_path, 'w') + file_default_pam.write(file_data) + file_default_pam.close() + + # Execute the commands that require for join Domain + (result_code, p_out, p_err) = self.util.execute("realm discover {}".format(domain_name.upper())) + if (result_code == 0): + self.logger.info("Realm Discover komutu başarılı") + else: + self.logger.error("Realm Discover komutu başarısız : " + str(p_err)) + + (result_code, p_out, p_err) = self.util.execute("echo \"{0}\" | realm join --user={1} {2}".format(password, ad_username, domain_name.upper())) + if (result_code == 0): + self.logger.info("Realm Join komutu başarılı") + else: + self.logger.error("Realm Join komutu başarısız : " + str(p_err)) + + # Configure sssd template + sssd_config_template_path = "/usr/share/ahenk/base/registration/config-files/sssd_ad.conf" + sssd_config_folder_path = "/etc/sssd" + sssd_config_file_path = "/etc/sssd/sssd.conf" + + if not self.util.is_exist(sssd_config_folder_path): + self.util.create_directory(sssd_config_folder_path) + self.logger.info("{0} folder is created".format(sssd_config_folder_path)) + + if self.util.is_exist(sssd_config_file_path): + self.util.delete_file(sssd_config_file_path) + self.logger.info("delete sssd org conf") + + self.util.copy_file(sssd_config_template_path, sssd_config_folder_path) + self.logger.info("{0} config file is copied under {1}".format(sssd_config_template_path, sssd_config_folder_path)) + self.util.rename_file("/etc/sssd/sssd_ad.conf", "/etc/sssd/sssd.conf") + + # Configure sssd.conf + file_sssd = open(sssd_config_file_path, 'r') + file_data = file_sssd.read() + + file_data = file_data.replace("###domains###", "domains = {}".format(domain_name)) + file_data = file_data.replace("###[domain/###", "[domain/{}]".format(domain_name)) + file_data = file_data.replace("###ad_domain###", "ad_domain = {}".format(domain_name)) + file_data = file_data.replace("###krb5_realm###", "krb5_realm = {}".format(domain_name.upper())) + + file_sssd.close() + file_sssd = open(sssd_config_file_path, 'w') + file_sssd.write(file_data) + file_sssd.close() + + # Arrangement of chmod as 600 for sssd.conf + (result_code, p_out, p_err) = self.util.execute("chmod 600 {}".format(sssd_config_file_path)) + if(result_code == 0): + self.logger.info("Chmod komutu başarılı bir şekilde çalıştırıldı") + else: + self.logger.error("Chmod komutu başarısız : " + str(p_err)) + + # Configure sssd for language environment + default_sssd_path = "/etc/default/sssd" + file_default_sssd = open(default_sssd_path, 'r') + file_data = file_default_sssd.read() + + if not self.util.is_exist(default_sssd_path): + self.util.create_directory(default_sssd_path) + self.logger.info("{0} folder is created".format(default_sssd_path)) + + if self.util.is_exist(default_sssd_path): + self.util.delete_file(default_sssd_path) + self.logger.info("delete sssd org conf") + + if "LC_ALL=\"tr_CY.UTF-8\"" not in file_data : + file_data = file_data + "\n" + "LC_ALL=\"tr_CY.UTF-8\"" + self.logger.info("/etc/default/sssd is configured") + + file_default_sssd.close() + file_default_sssd = open(default_sssd_path, 'w') + file_default_sssd.write(file_data) + file_default_sssd.close() + + self.util.execute("systemctl restart nscd.service") + # self.util.execute("pam-auth-update --force") + self.logger.info("AD Login operation has been completed.") + + self.logger.info("AD Login işlemi başarı ile sağlandı.") + return True + except Exception as e: + self.logger.error(str(e)) + self.logger.info("AD Login işlemi esnasında hata oluştu.") + return False + diff --git a/src/base/registration/execute_sssd_authentication.py b/src/base/registration/execute_sssd_authentication.py new file mode 100644 index 0000000..178b01b --- /dev/null +++ b/src/base/registration/execute_sssd_authentication.py @@ -0,0 +1,163 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +from base.scope import Scope +from base.util.util import Util +import re + + +class ExecuteSSSDAuthentication: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.util = Util() + + def authenticate(self, server_address, dn, admin_dn, admin_password): + try: + ldap_pwdlockout_dn = "cn=DefaultPolicy,ou=PasswordPolicies" + "," + dn + + # pattern for clearing file data from spaces, tabs and newlines + pattern = re.compile(r'\s+') + + sssd_config_template_path = "/usr/share/ahenk/base/registration/config-files/sssd.conf" + sssd_config_folder_path = "/etc/sssd" + sssd_config_file_path = "/etc/sssd/sssd.conf" + sssd_language_conf = "/etc/default/sssd" + + common_session_conf_path = "/etc/pam.d/common-session" + + # copy configuration file to /etc/sssd/sssd.conf before package installation + # create sssd folder in /etc + if not self.util.is_exist(sssd_config_folder_path): + self.util.create_directory(sssd_config_folder_path) + self.logger.info("{0} folder is created".format(sssd_config_folder_path)) + + # Copy sssd.conf template under /etc/sssd + self.util.copy_file(sssd_config_template_path, sssd_config_folder_path) + self.logger.info("{0} config file is copied under {1}".format(sssd_config_template_path, sssd_config_folder_path)) + + # Configure sssd.conf + file_sssd = open (sssd_config_file_path, 'r') + file_data = file_sssd.read() + + file_data = file_data.replace("###ldap_pwdlockout_dn###", "ldap_pwdlockout_dn = " + ldap_pwdlockout_dn) + file_data = file_data.replace("###ldap_uri###", "ldap_uri = " + "ldap://" + server_address + "/") + file_data = file_data.replace("###ldap_default_bind_dn###", "ldap_default_bind_dn = " + admin_dn) + file_data = file_data.replace("###ldap_default_authtok###", "ldap_default_authtok = " + admin_password) + file_data = file_data.replace("###ldap_search_base###", "ldap_search_base = " + dn) + file_data = file_data.replace("###ldap_user_search_base###", "ldap_user_search_base = " + dn) + file_data = file_data.replace("###ldap_group_search_base###", "ldap_group_search_base = " + dn) + file_data = file_data.replace("###ldap_sudo_search_base###", "ldap_sudo_search_base = ou=Roles," + dn) + + file_sssd.close() + file_sssd = open(sssd_config_file_path, 'w') + file_sssd.write(file_data) + file_sssd.close() + + # Install libpam-sss sssd-common for sssd authentication + (result_code, p_out, p_err) = self.util.execute("sudo apt install libpam-sss sssd-common -y") + + if result_code != 0: + self.logger.error("SSSD packages couldn't be downloaded.") + return False + + # configure common-session for creating home directories for ldap users + file_common_session = open(common_session_conf_path, 'r') + file_data = file_common_session.read() + + if "session optional pam_mkhomedir.so skel=/etc/skel umask=077" not in file_data : + file_data = file_data + "\n" + "session optional pam_mkhomedir.so skel=/etc/skel umask=077" + self.logger.info("common-session is configured") + + file_common_session.close() + file_common_session = open(common_session_conf_path, 'w') + file_common_session.write(file_data) + file_common_session.close() + + # configure sssd for language environment + file_default_sssd = open(sssd_language_conf, 'r') + file_data = file_default_sssd.read() + + if "LC_ALL=\"tr_CY.UTF-8\"" not in file_data : + file_data = file_data + "\n" + "LC_ALL=\"tr_CY.UTF-8\"" + self.logger.info("/etc/default/sssd is configured") + + file_default_sssd.close() + file_default_sssd = open(sssd_language_conf, 'w') + file_default_sssd.write(file_data) + file_default_sssd.close() + + self.logger.info("Restarting sssd service.") + self.util.execute("systemctl restart sssd.service") + + # Configure nsswitch.conf + file_ns_switch = open("/etc/nsswitch.conf", 'r') + file_data = file_ns_switch.read() + + # cleared file data from spaces, tabs and newlines + text = pattern.sub('', file_data) + + is_configuration_done_before = False + if "passwd:compatsss" not in text and "passwd:compat" in text: + file_data = file_data.replace("passwd: compat", "passwd: compat sss") + is_configuration_done_before = True + + if "passwd:filessystemdsss" not in text and "passwd:filessystemd" in text: + file_data = file_data.replace("passwd: files systemd", "passwd: files systemd sss") + is_configuration_done_before = True + + if "group:compatsss" not in text and "group:compat" in text: + file_data = file_data.replace("group: compat", "group: compat sss") + is_configuration_done_before = True + + if "group:filessystemdsss" not in text and "group:filessystemd" in text: + file_data = file_data.replace("group: files systemd", "group: files systemd sss") + is_configuration_done_before = True + + if "shadow:compatsss" not in text and "shadow:compat" in text: + file_data = file_data.replace("shadow: compat", "shadow: compat sss") + is_configuration_done_before = True + + if "shadow:filessss" not in text and "shadow:files" in text: + file_data = file_data.replace("shadow: files", "shadow: files sss") + is_configuration_done_before = True + + if "services:dbfilessss" not in text: + file_data = file_data.replace("services: db files", "services: db files sss") + is_configuration_done_before = True + + if "netgroup:nissss" not in text: + file_data = file_data.replace("netgroup: nis", "netgroup: nis sss") + is_configuration_done_before = True + + if "sudoers:filessss" not in text and "sudoers:files" in text: + file_data = file_data.replace("sudoers: files", "sudoers: files sss") + is_configuration_done_before = True + elif "sudoers:filessss" in text: + is_configuration_done_before = False + else: + file_data = file_data + "sudoers: files sss" + + + if is_configuration_done_before: + self.logger.info("nsswitch.conf configuration has been completed") + else: + self.logger.info("nsswitch.conf is already configured") + + file_ns_switch.close() + file_ns_switch = open("/etc/nsswitch.conf", 'w') + file_ns_switch.write(file_data) + file_ns_switch.close() + + self.util.execute("systemctl restart nscd.service") + # self.util.execute("pam-auth-update --force") + self.logger.info("LDAP Login operation has been completed.") + + self.logger.info("LDAP Login işlemi başarı ile sağlandı.") + return True + except Exception as e: + self.logger.error(str(e)) + self.logger.info("LDAP Login işlemi esnasında hata oluştu.") + return False + diff --git a/src/base/registration/registration.py b/src/base/registration/registration.py index b3e8709..7bfbc36 100644 --- a/src/base/registration/registration.py +++ b/src/base/registration/registration.py @@ -15,8 +15,11 @@ import pwd from base.timer.setup_timer import SetupTimer from base.timer.timer import Timer import re -import sys import os +from base.registration.execute_cancel_sssd_authentication import ExecuteCancelSSSDAuthentication +from base.registration.execute_sssd_authentication import ExecuteSSSDAuthentication +from base.registration.execute_sssd_ad_authentication import ExecuteSSSDAdAuthentication +from base.registration.execute_cancel_sssd_ad_authentication import ExecuteCancelSSSDAdAuthentication class Registration: def __init__(self): @@ -28,55 +31,65 @@ class Registration: self.conf_manager = scope.get_configuration_manager() self.db_service = scope.get_db_service() self.util = Util() - self.service_name='im.liderahenk.org' + self.servicename='im.liderahenk.org' + self.local_user_disable = None #self.event_manager.register_event('REGISTRATION_RESPONSE', self.registration_process) self.event_manager.register_event('REGISTRATION_SUCCESS', self.registration_success) self.event_manager.register_event('REGISTRATION_ERROR', self.registration_error) + self.ldap_login_cancel = ExecuteCancelSSSDAuthentication() + self.ad_login_cancel = ExecuteCancelSSSDAdAuthentication() + self.ldap_login = ExecuteSSSDAuthentication() + self.ad_login = ExecuteSSSDAdAuthentication() + + self.directory_server = None + if self.is_registered(): self.logger.debug('Ahenk already registered') else: self.register(True) - def registration_request(self): + def registration_request(self, hostname,username,password): self.logger.debug('Requesting registration') # SetupTimer.start(Timer(System.Ahenk.registration_timeout(), timeout_function=self.registration_timeout,checker_func=self.is_registered, kwargs=None)) - self.host = self.conf_manager.get("CONNECTION", "host") self.servicename = self.conf_manager.get("CONNECTION", "servicename") - self.user_name = '' - self.user_password= '' + self.host = hostname + self.user_name = username + self.user_password= password - user_name= os.getlogin() + if(username is None and password is None and self.host is None ): - self.logger.debug('User : '+ str(user_name)) + self.host = self.conf_manager.get("CONNECTION", "host") - pout = Util.show_registration_message(user_name,'Makineyi Lider MYS sistemine kaydetmek için bilgileri ilgili alanlara giriniz. LÜTFEN DEVAM EDEN İŞLEMLERİ SONLANDIRDIĞINZA EMİN OLUNUZ !', - 'LIDER MYS SISTEMINE KAYIT', self.host) + user_name= os.getlogin() + self.logger.debug('User : '+ str(user_name)) + pout = Util.show_registration_message(user_name,'Makineyi Lider MYS sistemine kaydetmek için bilgileri ilgili alanlara giriniz. LÜTFEN DEVAM EDEN İŞLEMLERİ SONLANDIRDIĞINZA EMİN OLUNUZ !', + 'LIDER MYS SISTEMINE KAYIT', self.host) + self.logger.debug('pout : ' + str(pout)) + field_values = pout.split(' ') + user_registration_info = list(field_values) - self.logger.debug('pout : ' + str(pout)) + if self.host == '': + self.host = user_registration_info[0] + self.user_name = user_registration_info[1] + self.user_password = user_registration_info[2] + self.directory_server = user_registration_info[3] - field_values = pout.split(' ') - - user_registration_info = list(field_values) - - if self.host == '' : - self.host = user_registration_info[0] - self.user_name = user_registration_info[1]; - self.user_password = user_registration_info[2]; - else: - self.user_name = user_registration_info[0]; - self.user_password = user_registration_info[1]; + else: + self.user_name = user_registration_info[0] + self.user_password = user_registration_info[1] + self.directory_server = user_registration_info[2] #anon_messenger = AnonymousMessenger(self.message_manager.registration_msg(user_name,user_password), self.host,self.servicename) #anon_messenger.connect_to_server() self.logger.debug('Requesting registration') SetupTimer.start(Timer(System.Ahenk.registration_timeout(), timeout_function=self.registration_timeout,checker_func=self.is_registered, kwargs=None)) - anon_messenger = AnonymousMessenger(self.message_manager.registration_msg(self.user_name,self.user_password), self.host,self.servicename) + anon_messenger = AnonymousMessenger(self.message_manager.registration_msg(self.user_name,self.user_password,self.directory_server), self.host,self.servicename) anon_messenger.connect_to_server() def ldap_registration_request(self): @@ -84,147 +97,59 @@ class Registration: self.messenger.send_Direct_message(self.message_manager.ldap_registration_msg()) def registration_success(self, reg_reply): - self.logger.info('Registration update starting') + try: + self.local_user_disable = reg_reply['disableLocalUser'] + if self.local_user_disable is True: + self.conf_manager.set('MACHINE', 'user_disabled', 'true') + else: + self.conf_manager.set('MACHINE', 'user_disabled', 'false') + + self.logger.info('LDAP Registration update starting') dn = str(reg_reply['agentDn']) self.logger.info('Current dn:' + dn) self.logger.info('updating host name and service') - self.install_and_config_ldap(reg_reply) self.update_registration_attrs(dn) + # lightdm configuration by desktop env is XFCE + self.desktop_env = self.util.get_desktop_env() + self.logger.info("Get desktop environment is {0}".format(self.desktop_env)) + if self.desktop_env == "xfce": + # Configure lightdm.service + # check if 99-pardus-xfce.conf exists if not create + pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" + if not self.util.is_exist(pardus_xfce_path): + self.logger.info("99-pardus-xfce.conf does not exist.") + self.util.create_file(pardus_xfce_path) + + file_lightdm = open(pardus_xfce_path, 'a') + file_lightdm.write("[Seat:*]\n") + file_lightdm.write("greeter-hide-users=true") + file_lightdm.close() + self.logger.info("lightdm has been configured.") + else: + self.logger.info("99-pardus-xfce.conf exists. Delete file and create new one.") + self.util.delete_file(pardus_xfce_path) + self.util.create_file(pardus_xfce_path) + + file_lightdm = open(pardus_xfce_path, 'a') + file_lightdm.write("[Seat:*]") + file_lightdm.write("greeter-hide-users=true") + file_lightdm.close() + self.logger.info("lightdm.conf has been configured.") + + # LDAP registration + if self.directory_server == "LDAP": + self.install_and_config_ldap(reg_reply) + # AD registration + else: + self.install_and_config_ad(reg_reply) + except Exception as e: - self.logger.error('Registartion error. Error Message: {0}.'.format(str(e))) + self.logger.error('Registration error. Error Message: {0}.'.format(str(e))) print(e) raise - def install_and_config_ldap(self, reg_reply): - self.logger.info('ldap install process starting') - server_address = str(reg_reply['ldapServer']) - dn = str(reg_reply['ldapBaseDn']) - version = str(reg_reply['ldapVersion']) - admin_dn = str(reg_reply['ldapUserDn']) # get user full dn from server.. password same - admin_password = self.user_password # same user get from server - - if server_address != '' and dn != '' and version != '' and admin_dn != '' and admin_password != '': - (result_code, p_out, p_err) = self.util.execute("/bin/bash /usr/share/ahenk/plugins/ldap-login/scripts/ldap-login.sh {0} {1} {2} {3} {4}".format( - server_address, "\'" + dn + "\'", "\'" + admin_dn + "\'", "\'" + admin_password + "\'", version)) - if result_code == 0: - self.logger.info("Script has run successfully") - self.change_pam_ldap_configs() - else: - self.logger.error("Script could not run successfully: " + p_err) - print("ERROR ---> " + str(p_err)) - raise Exception('LDAP Ayarları yapılırken hata oluştu. Lütfen ağ bağlantınızı kontrol ediniz. Deponuzun güncel olduğundan emin olunuz.') - else : - raise Exception( - 'LDAP Ayarları yapılırken hata oluştu. Lütfen ağ bağlantınızı kontrol ediniz. Deponuzun güncel olduğundan emin olunuz.') - - - - def registration_error(self, reg_reply): - self.re_register() - - - def change_pam_ldap_configs(self): - # pattern for clearing file data from spaces, tabs and newlines - pattern = re.compile(r'\s+') - - pam_scripts_original_directory_path = "/usr/share/ahenk/pam_scripts_original" - - ldap_back_up_file_path = "/usr/share/ahenk/pam_scripts_original/ldap" - ldap_original_file_path = "/usr/share/pam-configs/ldap" - ldap_configured_file_path = "/usr/share/ahenk/plugins/ldap-login/config-files/ldap" - - pam_script_back_up_file_path = "/usr/share/ahenk/pam_scripts_original/pam_script" - pam_script_original_file_path = "/usr/share/pam-configs/pam_script" - pam_script_configured_file_path = "/usr/share/ahenk/plugins/ldap-login/config-files/pam_script" - - #create pam_scripts_original directory if not exists - if not self.util.is_exist(pam_scripts_original_directory_path): - self.logger.info("Creating {0} directory.".format(pam_scripts_original_directory_path)) - self.util.create_directory(pam_scripts_original_directory_path) - - if self.util.is_exist(ldap_back_up_file_path): - self.logger.info("Changing {0} with {1}.".format(ldap_original_file_path, ldap_configured_file_path)) - self.util.copy_file(ldap_configured_file_path, ldap_original_file_path) - else: - self.logger.info("Backing up {0}".format(ldap_original_file_path)) - self.util.copy_file(ldap_original_file_path, ldap_back_up_file_path) - self.logger.info("{0} file is replaced with {1}.".format(ldap_original_file_path, ldap_configured_file_path)) - self.util.copy_file(ldap_configured_file_path, ldap_original_file_path) - - if self.util.is_exist(pam_script_back_up_file_path): - self.util.copy_file(pam_script_configured_file_path, pam_script_original_file_path) - self.logger.info("{0} is replaced with {1}.".format(pam_script_original_file_path, pam_script_configured_file_path)) - else: - self.logger.info("Backing up {0}".format(pam_script_original_file_path)) - self.util.copy_file(pam_script_original_file_path, pam_script_back_up_file_path) - self.logger.info("{0} file is replaced with {1}".format(pam_script_original_file_path, pam_script_configured_file_path)) - self.util.copy_file(pam_script_configured_file_path, pam_script_original_file_path) - - (result_code, p_out, p_err) = self.util.execute("DEBIAN_FRONTEND=noninteractive pam-auth-update --package") - if result_code == 0: - self.logger.info("'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' has run successfully") - else: - self.logger.error("'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' could not run successfully: " + p_err) - - - # Configure nsswitch.conf - file_ns_switch = open("/etc/nsswitch.conf", 'r') - file_data = file_ns_switch.read() - - # cleared file data from spaces, tabs and newlines - text = pattern.sub('', file_data) - - is_configuration_done_before = False - if ("passwd:compatldap" not in text): - file_data = file_data.replace("passwd: compat", "passwd: compat ldap") - is_configuration_done_before = True - - if ("group:compatldap" not in text): - file_data = file_data.replace("group: compat", "group: compat ldap") - is_configuration_done_before = True - - if ("shadow:compatldap" not in text): - file_data = file_data.replace("shadow: compat", "shadow: compat ldap") - is_configuration_done_before = True - - if is_configuration_done_before: - self.logger.info("nsswitch.conf configuration has been completed") - else: - self.logger.info("nsswitch.conf is already configured") - - file_ns_switch.close() - file_ns_switch = open("/etc/nsswitch.conf", 'w') - file_ns_switch.write(file_data) - file_ns_switch.close() - - # Configure lightdm.service - # check if 99-pardus-xfce.conf exists if not create - pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" - if not self.util.is_exist(pardus_xfce_path): - self.logger.info("99-pardus-xfce.conf does not exist.") - self.util.create_file(pardus_xfce_path) - - file_lightdm = open(pardus_xfce_path, 'a') - file_lightdm.write("[Seat:*]\n") - file_lightdm.write("greeter-hide-users=true") - file_lightdm.close() - self.logger.info("lightdm has been configured.") - else: - self.logger.info("99-pardus-xfce.conf exists. Delete file and create new one.") - self.util.delete_file(pardus_xfce_path) - self.util.create_file(pardus_xfce_path) - - file_lightdm = open(pardus_xfce_path, 'a') - file_lightdm.write("[Seat:*]") - file_lightdm.write("greeter-hide-users=true") - file_lightdm.close() - self.logger.info("lightdm.conf has been configured.") - self.util.execute("systemctl restart nscd.service") - self.logger.info("Operation finished") - - def update_registration_attrs(self, dn=None): self.logger.debug('Registration configuration is updating...') self.db_service.update('registration', ['dn', 'registered'], [dn, 1], ' registered = 0') @@ -244,7 +169,39 @@ class Registration: self.conf_manager.write(configfile) self.logger.debug('Registration configuration file is updated') + def install_and_config_ldap(self, reg_reply): + self.logger.info('ldap install process starting') + server_address = str(reg_reply['ldapServer']) + dn = str(reg_reply['ldapBaseDn']) + version = str(reg_reply['ldapVersion']) + admin_dn = str(reg_reply['ldapUserDn']) # get user full dn from server.. password same + #admin_password = self.user_password # same user get from server + admin_password = self.db_service.select_one_result('registration', 'password', ' registered=1') + self.ldap_login.authenticate(server_address, dn, admin_dn, admin_password) + if server_address != '' and dn != '' and version != '' and admin_dn != '' and admin_password != '': + self.logger.info("SSSD configuration process starting....") + self.logger.info("SSSD configuration process starting....") + else : + raise Exception( + 'LDAP Ayarları yapılırken hata oluştu. Lütfen ağ bağlantınızı kontrol ediniz. Deponuzun güncel olduğundan emin olunuz.') + + def install_and_config_ad(self, reg_reply): + self.logger.info('AD install process starting') + domain_name = str(reg_reply['adDomainName']) + host_name = str(reg_reply['adHostName']) + ip_address = str(reg_reply['adIpAddress']) + password = str(reg_reply['adAdminPassword']) + ad_username = str(reg_reply['adAdminUserName']) + + if domain_name is None or host_name is None or ip_address is None or password is None : + self.logger.error("Registration params is null") + return + + self.ad_login.authenticate(domain_name, host_name, ip_address, password, ad_username) + + def registration_error(self, reg_reply): + self.re_register() def is_registered(self): try: @@ -349,43 +306,50 @@ class Registration: 'and it is connected to XMPP server! Check your Ahenk configuration file (/etc/ahenk/ahenk.conf)') self.logger.error('Ahenk is shutting down...') print('Ahenk is shutting down...') - Util.show_message(os.getlogin(),':0',"Lider MYS sistemine ulaşılamadı. Lütfen sunucu adresini kontrol ediniz....","HATA") - System.Process.kill_by_pid(int(System.Ahenk.get_pid_number())) - - def purge_and_unregister(self): try: - - self.logger.info('Ahenk conf cleaned') self.logger.info('Ahenk conf cleaning from db') self.unregister() - self.logger.info('Purge ldap packages') - Util.execute("sudo apt purge libpam-ldap libnss-ldap ldap-utils -y") - # self.logger.info('Purge ahenk packages') - # Util.execute("sudo apt purge ahenk ahenk-* -y") - Util.execute("sudo apt autoremove -y") - self.change_configs_after_purge() - self.logger.info('purging successfull') + + directory_type = "LDAP" + if self.util.is_exist("/etc/ahenk/ad_info"): + directory_type = "AD" + + if directory_type == "LDAP": + self.ldap_login_cancel.cancel() + else: + self.ad_login_cancel.cancel() + self.logger.info('Cleaning ahenk conf..') self.clean() self.logger.info('Ahenk conf cleaned from db') - self.logger.info('Enable Users') - self.enable_local_users() - Util.shutdown() + if self.conf_manager.has_section('MACHINE'): + user_disabled = self.conf_manager.get("MACHINE", "user_disabled") + self.logger.info('User disabled value=' + str(user_disabled)) + if user_disabled != 'false': + self.logger.info('Enable Users') + self.enable_local_users() + else: + self.logger.info('Local users already enabled') + # İf desktop env is XFCE configured lightdm.service + if self.util.get_desktop_env() == "xfce": + pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" + if self.util.is_exist(pardus_xfce_path): + self.logger.info("99-pardus-xfce.conf exists. Deleting file.") + self.util.delete_file(pardus_xfce_path) + + Util.shutdown() except Exception as e: self.logger.error("Error while running purge_and_unregister process.. Error Message " + str(e)) - - #System.Process.kill_by_pid(int(System.Ahenk.get_pid_number())) #sys.exit(2) def change_configs_after_purge(self): - # pattern for clearing file data from spaces, tabs and newlines pattern = re.compile(r'\s+') @@ -402,7 +366,8 @@ class Registration: self.util.delete_file(ldap_back_up_file_path) if self.util.is_exist(pam_script_back_up_file_path): - self.logger.info("Replacing {0} with {1}".format(pam_script_original_file_path, pam_script_back_up_file_path)) + self.logger.info( + "Replacing {0} with {1}".format(pam_script_original_file_path, pam_script_back_up_file_path)) self.util.copy_file(pam_script_back_up_file_path, pam_script_original_file_path) self.logger.info("Deleting {0}".format(pam_script_back_up_file_path)) self.util.delete_file(pam_script_back_up_file_path) @@ -411,7 +376,8 @@ class Registration: if result_code == 0: self.logger.info("'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' has run successfully") else: - self.logger.error("'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' could not run successfully: " + p_err) + self.logger.error( + "'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' could not run successfully: " + p_err) # Configure nsswitch.conf file_ns_switch = open("/etc/nsswitch.conf", 'r') @@ -421,18 +387,22 @@ class Registration: text = pattern.sub('', file_data) did_configuration_change = False - if "passwd:compatldap" in text: - file_data = file_data.replace("passwd: compat ldap", "passwd: compat") + if "passwd:compatldap[NOTFOUND=return]db" in text: + file_data = file_data.replace("passwd: compat ldap [NOTFOUND=return] db", "passwd: compat") did_configuration_change = True - if "group:compatldap" in text: - file_data = file_data.replace("group: compat ldap", "group: compat") + if "group:compatldap[NOTFOUND=return]db" in text: + file_data = file_data.replace("group: compat ldap [NOTFOUND=return] db", "group: compat") did_configuration_change = True if "shadow:compatldap" in text: file_data = file_data.replace("shadow: compat ldap", "shadow: compat") did_configuration_change = True + if "#gshadow:files" in text: + file_data = file_data.replace("#gshadow: files", "gshadow: files") + did_configuration_change = True + if did_configuration_change: self.logger.info("nsswitch.conf configuration has been configured") else: @@ -443,6 +413,12 @@ class Registration: file_ns_switch.write(file_data) file_ns_switch.close() + # Configure ldap-cache + nss_update_cron_job_file_path = "/etc/cron.daily/nss-updatedb" + if self.util.is_exist(nss_update_cron_job_file_path): + self.util.delete_file(nss_update_cron_job_file_path) + self.logger.info("{0} is deleted.".format(nss_update_cron_job_file_path)) + # Configure lightdm.service pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" if self.util.is_exist(pardus_xfce_path): @@ -452,7 +428,6 @@ class Registration: self.util.execute("systemctl restart nscd.service") self.logger.info("Operation finished") - def clean(self): print('Ahenk cleaning..') import configparser @@ -473,7 +448,7 @@ class Registration: config.set('CONNECTION', 'uid', '') config.set('CONNECTION', 'password', '') - config.set('MACHINE', 'user_disabled', '0') + config.set('MACHINE', 'user_disabled', 'false') with open(System.Ahenk.config_path(), 'w') as file: config.write(file) @@ -508,6 +483,20 @@ class Registration: change_username = 'usermod -l {0} {1}' content = Util.read_file('/etc/passwd') kill_all_process = 'killall -KILL -u {}' + change_permisson = "chmod -R 700 {}" + + add_user_conf_file = "/etc/adduser.conf" + file_dir_mode = open(add_user_conf_file, 'r') + file_data = file_dir_mode.read() + file_data = file_data.replace("DIR_MODE=0755", "DIR_MODE=0700") + file_dir_mode.close() + + file_dir_mode = open(add_user_conf_file, 'w') + file_dir_mode.write(file_data) + file_dir_mode.close() + + self.logger.info("add user mode changed to 0700 in file {}".format(add_user_conf_file)) + for p in pwd.getpwall(): self.logger.info("User: '{0}' will be disabled and changed username and home directory of username".format(p.pw_name)) if not sysx.shell_is_interactive(p.pw_shell): @@ -521,3 +510,4 @@ class Registration: Util.execute(passwd_cmd.format(p.pw_name)) Util.execute(change_username.format(new_username, p.pw_name)) Util.execute(change_home.format(new_home_dir, new_username)) + Util.execute(change_permisson.format(new_home_dir)) diff --git a/src/base/registration/scripts/ad.sh b/src/base/registration/scripts/ad.sh new file mode 100644 index 0000000..2298d05 --- /dev/null +++ b/src/base/registration/scripts/ad.sh @@ -0,0 +1,26 @@ +#!/bin/bash +#set debconf krb5 and samba-common + +ad_domain_name=$1 +ad_host_name=$2 + +echo "samba-common samba-common/workgroup string WORKGROUP" | sudo debconf-set-selections +echo "samba-common samba-common/dhcp boolean false" | sudo debconf-set-selections +echo "samba-common samba-common/do_debconf boolean true" | sudo debconf-set-selections +apt-get -y install samba-common + + +cat > /root/debconf-krb5.conf < +#set debconf libnss-ldap and libpam-ldap + +ldap_hostname=$1 +ldap_base_dn=$2 +ldap_user_dn=$3 +ldap_user_pwd=$4 +ldap_version=$5 + +echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections + +## libnss-ldap +echo -e " \ +libnss-ldap libnss-ldap/dblogin boolean false +libnss-ldap shared/ldapns/base-dn string $ldap_base_dn +libnss-ldap libnss-ldap/binddn string $ldap_user_dn +libnss-ldap libnss-ldap/dbrootlogin boolean true +libnss-ldap libnss-ldap/override boolean true +libnss-ldap shared/ldapns/ldap-server string $ldap_hostname +libnss-ldap libnss-ldap/confperm boolean false +libnss-ldap libnss-ldap/rootbinddn string $ldap_user_dn +libnss-ldap shared/ldapns/ldap_version select $ldap_version +libnss-ldap libnss-ldap/nsswitch note +libpam-ldap libpam-ldap/dblogin boolean false +libpam-ldap libpam-ldap/dbrootlogin boolean true +libpam-ldap libpam-ldap/override boolean true +libpam-ldap libpam-ldap/pam_password string crypt +libpam-ldap libpam-ldap/rootbinddn string $ldap_user_dn +libpam-ldap libpam-runtime/override boolean false \ +" | debconf-set-selections + +echo "Name: libnss-ldap/bindpw +Template: libnss-ldap/bindpw +Owners: libnss-ldap, libnss-ldap:amd64 + +Name: libnss-ldap/rootbindpw +Template: libnss-ldap/rootbindpw +Value: +Owners: libnss-ldap, libnss-ldap:amd64 +Flags: seen + +Name: libpam-ldap/bindpw +Template: libpam-ldap/bindpw +Owners: libpam-ldap, libpam-ldap:amd64 + +Name: libpam-ldap/rootbindpw +Template: libpam-ldap/rootbindpw +Value: +Owners: libpam-ldap, libpam-ldap:amd64 +Flags: seen +Variables: + filename = /etc/pam_ldap.secret + package = libpam-ldap" >> /var/cache/debconf/passwords.dat + +echo $ldap_user_pwd > /etc/pam_ldap.secret +apt update +apt-get install libpam-ldap libnss-ldap ldap-utils -y +SUDO_FORCE_REMOVE=yes apt-get install sudo-ldap -y \ No newline at end of file diff --git a/src/base/registration/test.py b/src/base/registration/test.py new file mode 100644 index 0000000..52b6e88 --- /dev/null +++ b/src/base/registration/test.py @@ -0,0 +1,91 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +from base.scope import Scope +from base.util.util import Util +import re + + +class ExecuteCancelSSSDAuthentication: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.util = Util() + + def cancel(self): + self.util.execute("apt purge libpam-sss sssd-common -y") + self.util.execute("apt autoremove -y") + + if self.util.is_exist("/etc/sssd"): + self.util.delete_folder("/etc/sssd") + + # pattern for clearing file data from spaces, tabs and newlines + pattern = re.compile(r'\s+') + + # Configure nsswitch.conf + file_ns_switch = open("/etc/nsswitch.conf", 'r') + file_data = file_ns_switch.read() + + # cleared file data from spaces, tabs and newlines + text = pattern.sub('', file_data) + + did_configuration_change = False + if "passwd:compatsss" in text: + file_data = file_data.replace("passwd: compat sss", "passwd: compat") + did_configuration_change = True + + if "group:compatsss" in text: + file_data = file_data.replace("group: compat sss", "group: compat") + did_configuration_change = True + + if "shadow:compatsss" in text: + file_data = file_data.replace("shadow: compat sss", "shadow: compat") + did_configuration_change = True + + if "services:dbfilessss" in text: + file_data = file_data.replace("services: db files sss", "services: db files") + did_configuration_change = True + + if "netgroup:nissss" in text: + file_data = file_data.replace("netgroup: nis sss", "netgroup: nis") + did_configuration_change = True + + if "sudoers:filessss" in text: + file_data = file_data.replace("sudoers: files sss", "") + did_configuration_change = True + + if did_configuration_change: + self.logger.info("nsswitch.conf configuration has been configured") + else: + self.logger.info("nsswitch.conf has already been configured") + + file_ns_switch.close() + file_ns_switch = open("/etc/nsswitch.conf", 'w') + file_ns_switch.write(file_data) + file_ns_switch.close() + + common_session_conf_path = "/etc/pam.d/common-session" + + # configure common-session for creating home directories for ldap users + file_common_session = open(common_session_conf_path, 'r') + file_data = file_common_session.read() + + if "session optional pam_mkhomedir.so skel=/etc/skel umask=077" in file_data: + file_data = file_data.replace("session optional pam_mkhomedir.so skel=/etc/skel umask=077", "") + self.logger.info("common-session is configured") + + file_common_session.close() + file_common_session = open(common_session_conf_path, 'w') + file_common_session.write(file_data) + file_common_session.close() + + # Configure lightdm.service + pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" + if self.util.is_exist(pardus_xfce_path): + self.logger.info("99-pardus-xfce.conf exists. Deleting file.") + self.util.delete_file(pardus_xfce_path) + self.util.execute("systemctl restart nscd.service") + + self.logger.info("LDAP Login iptal etme işlemi başarı ile sağlandı.") + diff --git a/src/base/util/util.py b/src/base/util/util.py index 4404aa5..c8dec3e 100644 --- a/src/base/util/util.py +++ b/src/base/util/util.py @@ -17,16 +17,21 @@ from base.scope import Scope class Util: + + def __init__(self): super().__init__() + @staticmethod + def get_ask_path_file(): + return '/usr/share/ahenk/base/agreement/' + @staticmethod def close_session(username): Util.execute('pkill -9 -u {0}'.format(username)) @staticmethod def shutdown(): - print("shutting down") Util.execute('reboot') @staticmethod @@ -138,6 +143,16 @@ class Util: except: raise + @staticmethod + def get_executable_path(app_name): + path = None + try: + path = shutil.which(app_name) + except: + raise + finally: + return path + @staticmethod def execute(command, stdin=None, env=None, cwd=None, shell=True, result=True, as_user=None, ip=None): @@ -182,7 +197,7 @@ class Util: command.append(script_path) else: raise Exception('[Util] Script is required') - if parameters is not None: + if parameters is not None: for p in parameters: command.append(p) @@ -225,8 +240,9 @@ class Util: def file_group(full_path): try: st = os.stat(full_path) - gid = st.st_uid - return grp.getgrgid(gid)[0] + gid = st.st_gid + # return grp.getgrgid(gid)[0] + return gid except: raise @@ -332,15 +348,20 @@ class Util: Util.execute('export DISPLAY={0}; su - {1} -c \'{2}\''.format(display, user, inner_command)) @staticmethod - def show_message(username,display=':0',message='', title=''): - ask_path = '/usr/share/ahenk/base/agreement/confirm.py' + def show_message(username, display, message='', title=''): + ask_path = Util.get_ask_path_file()+ 'confirm.py' + + Scope.get_instance().get_logger().debug('DISPLAYYYY --------->>>>>>>>: ' + str(display)) + + if display is None: + display_number = Util.get_username_display() + else: + display_number = display try: if username is not None: - command = 'export DISPLAY={0};su - {1} -c \'python3 {2} \"{3}\" \"{4}\"\''.format(display, username, - ask_path, - message, - title) + command = 'su - {0} -c \'python3 {1} \"{2}\" \"{3}\" \"{4}\"\''.format(username, ask_path, message, + title, display_number) result_code, p_out, p_err = Util.execute(command) if p_out.strip() == 'Y': @@ -355,22 +376,26 @@ class Util: except Exception as e : print("Error when showing message " + str(e)) - return None; + return None + + @staticmethod def show_registration_message(login_user_name,message,title,host=None): - ask_path = '/usr/share/ahenk/base/agreement/ahenkmessage.py' - display_number = ":0" + + ask_path = Util.get_ask_path_file()+ 'ahenkmessage.py' + + # display_number = ":0" + display_number = Util.get_username_display() if host is None: - command = 'export DISPLAY={0}; su - {1} -c \"python3 {2} \'{3}\' \'{4}\' \"'.format(display_number, login_user_name, - ask_path, message, title) + command = 'su - {0} -c \"python3 {1} \'{2}\' \'{3}\' \'{4}\' \"'.format(login_user_name, + ask_path, message, title, display_number) else: - command = 'export DISPLAY={0}; su - {1} -c \"python3 {2} \'{3}\' \'{4}\' \'{5}\' \"'.format(display_number, - login_user_name, + command = 'su - {0} -c \"python3 {1} \'{2}\' \'{3}\' \'{4}\' \'{5}\' \"'.format(login_user_name, ask_path, message, title, - host) + host, display_number) result_code, p_out, p_err = Util.execute(command) pout = str(p_out).replace('\n', '') @@ -380,16 +405,42 @@ class Util: @staticmethod def show_unregistration_message(login_user_name,display_number,message,title): - ask_path = '/usr/share/ahenk/base/agreement/unregistrationmessage.py' + ask_path = Util.get_ask_path_file()+ 'unregistrationmessage.py' - command = 'export DISPLAY={0}; su - {1} -c \"python3 {2} \'{3}\' \'{4}\' \"'.format(display_number, - login_user_name, - ask_path, - message, title - ) + command = 'su - {0} -c \"python3 {1} \'{2}\' \'{3}\' \'{4}\' \"'.format(login_user_name, ask_path, message, title, display_number) result_code, p_out, p_err = Util.execute(command) pout = str(p_out).replace('\n', '') return pout + @staticmethod + def get_username_display(): + result_code, p_out, p_err = Util.execute("who | awk '{print $1, $5}' | sed 's/(://' | sed 's/)//'", result=True) + + result = [] + lines = str(p_out).split('\n') + for line in lines: + arr = line.split(' ') + if len(arr) > 1 and str(arr[1]).isnumeric() is True: + result.append(line) + + params = str(result[0]).split(' ') + display_number = params[1] + display_number = ":"+str(display_number) + return display_number + + @staticmethod + def get_desktop_env(): + xfce4_session = "/usr/bin/xfce4-session" + gnome_session = "/usr/bin/gnome-session" + desktop_env = None + result_code, p_out, p_err = Util.execute("ls {}".format(gnome_session)) + if result_code == 0: + desktop_env = "gnome" + result_code, p_out, p_err = Util.execute("ls {}".format(xfce4_session)) + if result_code == 0: + desktop_env = "xfce" + + return desktop_env + diff --git a/src/plugins/browser/main.py b/src/plugins/browser/main.py new file mode 100644 index 0000000..6704d94 --- /dev/null +++ b/src/plugins/browser/main.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: > +# Author: Volkan Şahin + + +def info(): + inf = dict() + inf['name'] = 'browser' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'Browser plugin provides to managing user or global firefox profile like setting homepage etc.' + inf['task'] = True + inf['user_oriented'] = True + inf['machine_oriented'] = True + inf['developer'] = 'bm.volkansahin@gmail.com' + + return inf diff --git a/src/plugins/browser/policy.py b/src/plugins/browser/policy.py new file mode 100644 index 0000000..6c51fea --- /dev/null +++ b/src/plugins/browser/policy.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin +# Author: Tuncay Çolak + +import json +from base.plugin.abstract_plugin import AbstractPlugin + +class Browser(AbstractPlugin): + """docstring for Browser""" + + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + self.mozilla_config_file = 'mozilla.cfg' + self.local_settings_JS_file = 'local-settings.js' + self.local_settings_JS_path = 'defaults/pref/' + self.logger.info('Parameters were initialized.') + + def handle_policy(self): + self.logger.info('Browser plugin handling...') + try: + username = self.context.get('username') + self.logger.info('Username: {}'.format(username)) + if username is not None: + self.logger.debug('Writing preferences to user profile') + self.write_to_user_profile(username) + self.context.create_response(code=self.message_code.POLICY_PROCESSED.value, message='Kullanıcı browser profili başarıyla uygulandı.') + else: + self.logger.debug('Writing preferences to global profile') + self.write_to_global_profile() + self.context.create_response(code=self.message_code.POLICY_PROCESSED.value, message='Ajan browser profili başarıyla uygulandı.') + self.logger.info('Browser profile is handled successfully') + except Exception as e: + self.logger.error('A problem occurred while handling browser profile: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.POLICY_ERROR.value, message='Browser profili uygulanırken bir hata oluştu.') + + def write_to_user_profile(self, username): + + try: + username = str(username).strip() + profile_paths = self.find_user_preference_paths(username) + if profile_paths is not None: + # User might have multiple firefox profile directories + for path in profile_paths: + if self.is_exist(path): + path = str(path) + '/user.js' + user_jss = open(path, 'w') + preferences = json.loads(self.data)['preferences'] + self.logger.debug('Writing preferences to user.js file ...') + for pref in preferences: + if pref['value'].isdigit() or str(pref['value']) == 'false' or str(pref['value']) == 'true': + value = pref['value'] + else: + value = '\"' + pref['value'] + '\"' + line = 'user_pref("' + str(pref['preferenceName']) + '",' + value + ');\n' + user_jss.write(line) + + self.logger.debug('User preferences were wrote successfully') + user_jss.close() + change_owner = 'chown ' + username + ':' + username + ' ' + path + self.execute(change_owner) + self.logger.debug('Preferences file owner is changed') + + except Exception as e: + self.logger.error('A problem occurred while writing user profile: {0}'.format(str(e))) + # Remove global lock files to tell Firefox to load the user + + installation_path_list = self.find_firefox_installation_path() + for installation_path in installation_path_list: + if installation_path is None: + self.logger.error('Firefox installation directory could not be found! Finishing task...') + return + self.silent_remove(str(installation_path) + self.mozilla_config_file) + self.silent_remove(str(installation_path) + self.local_settings_JS_path + self.local_settings_JS_file) + self.logger.debug('User profiles have been set successfully') + + def write_to_global_profile(self): + + firefox_installation_path_list = self.find_firefox_installation_path() + + if firefox_installation_path_list is not None: + for firefox_installation_path in firefox_installation_path_list: + preferences = None + try: + preferences = json.loads(str(self.data))['preferences'] + except Exception as e: + self.logger.error('Problem occurred while getting preferences. Error Message: {}'.format(str(e))) + + mozilla_cfg = open(str(firefox_installation_path) + self.mozilla_config_file, 'w') + self.logger.debug('Mozilla configuration file is created for {0}'.format(firefox_installation_path)) + # mozilla.cfg file must start with command + is_command_line_added = False + for pref in preferences: + if pref['value'].isdigit() or str(pref['value']) == 'false' or str(pref['value']) == 'true': + value = pref['value'] + else: + value = '\"' + pref['value'] + '\"' + line = 'lockPref("' + str(pref['preferenceName']) + '",' + value + ');\n' + if not is_command_line_added: + mozilla_cfg.write("//mozilla.cfg must start with command.\n") + is_command_line_added = True + mozilla_cfg.write(line) + mozilla_cfg.close() + self.logger.debug('Preferences were wrote to Mozilla configuration file for {0}'.format(firefox_installation_path)) + + local_settings_path = str(firefox_installation_path) + self.local_settings_JS_path + if not self.is_exist(local_settings_path): + self.logger.debug('Firefox local setting path not found, it will be created') + self.create_directory(local_settings_path) + local_settings_js = open(local_settings_path + self.local_settings_JS_file, 'w') + local_settings_js.write( + 'pref("general.config.obscure_value", 0);\npref("general.config.filename", "mozilla.cfg");\n') + local_settings_js.close() + self.logger.debug('Firefox local settings were configured {}'.format(firefox_installation_path)) + + + def silent_remove(self, filename): + try: + if self.is_exist(filename): + self.delete_file(filename) + self.logger.debug('{0} removed successfully'.format(filename)) + else: + self.logger.warning('{0} was tried to delete but not found.'.format(filename)) + except Exception as e: + self.logger.error('Problem occurred while removing file {0}. Exception Message is: {1}'.format(filename, str(e))) + + def find_user_preference_paths(self, user_name): + + paths = [] + firefox_path = '/home/' + user_name + '/.mozilla/firefox/' + if self.is_exist(firefox_path + 'profiles.ini'): + profile_ini_file = open(firefox_path + 'profiles.ini', 'r') + profile_ini_file_lines = profile_ini_file.readlines() + for line in profile_ini_file_lines: + if 'Path' in line: + paths.append(firefox_path + str(line.split('=')[1]).strip()) + if len(paths) > 0: + self.logger.debug('User preferences path found successfully') + return paths + else: + self.logger.error('User preferences path not found') + + def find_firefox_installation_path(self): + + installation_path_list = [] + if self.is_exist("/usr/lib/firefox-esr/"): + installation_path_list.append("/usr/lib/firefox-esr/") + + if self.is_exist('/opt/firefox-esr/'): + installation_path_list.append('/opt/firefox-esr/') + + if self.is_exist('/usr/lib/iceweasel/'): + installation_path_list.append('/usr/lib/iceweasel/') + + if self.is_exist('/opt/firefox/'): + installation_path_list.append('/opt/firefox/') + + if installation_path_list: + self.logger.info("Firefox installation paths list: "+str(installation_path_list)) + return installation_path_list + + else: + self.logger.error('Firefox installation path not found') + return None + + +def handle_policy(profile_data, context): + browser = Browser(profile_data, context) + browser.handle_policy() diff --git a/src/plugins/conky/ask.py b/src/plugins/conky/ask.py new file mode 100644 index 0000000..4820def --- /dev/null +++ b/src/plugins/conky/ask.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin +# Author: Tuncay ÇOLAK + +import sys +import easygui + + +def ask(content, title): + choice = easygui.buttonbox(msg=title, title=content, choices=['TAMAM']) + #choice = easygui.textbox(msg=title, text=content) + if choice: + print('Y') + else: + print('N') + + +if __name__ == '__main__': + + if len(sys.argv) == 3: + try: + ask(sys.argv[1], sys.argv[2]) + except Exception as e: + print(str(e)) + else: + print('Argument fault. Check your parameters or content of parameters. Parameters: ' + str(sys.argv)) \ No newline at end of file diff --git a/src/plugins/conky/execute_conky.py b/src/plugins/conky/execute_conky.py new file mode 100644 index 0000000..0901e34 --- /dev/null +++ b/src/plugins/conky/execute_conky.py @@ -0,0 +1,146 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Edip YILDIZ + + +from base.model.enum.content_type import ContentType +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class RunConkyCommand(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + self.conky_config_file_dir = '/etc/conky' + self.conky_config_global_autorun_file = '/etc/xdg/autostart/conky.desktop' + self.conky_config_file_path = self.conky_config_file_dir + '/conky.conf' + self.logger.debug('[Conky] Parameters were initialized.') + self.conky_autorun_content = '[Desktop Entry] \n' \ + 'Comment[tr]= \n' \ + 'Comment= \n' \ + 'Exec=conky_wp \n' \ + 'GenericName[tr]= \n' \ + 'GenericName= \n' \ + 'Icon=system-run \n' \ + 'MimeType= \n' \ + 'Name[tr]= \n' \ + 'Name= \n' \ + 'Path= \n' \ + 'StartupNotify=true \n' \ + 'Terminal=false \n' \ + 'TerminalOptions= \n' \ + 'Type=Application \n' \ + 'X-DBUS-ServiceName= \n' \ + 'X-DBUS-StartupType= \n' \ + 'X-KDE-SubstituteUID=false \n' \ + 'X-KDE-Username= \n' + + self.conky_wrapper_file= '/usr/bin/conky_wp' + + self.conky_wrapper_content = '#!/bin/bash \n' \ + ' killall conky \n' \ + ' sleep 5 \n' \ + ' /usr/bin/conky -q \n' + + def remove_conky_message(self): + self.execute("killall conky") + if self.is_exist(self.conky_config_global_autorun_file) == True: + self.delete_file(self.conky_config_global_autorun_file) + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Conky measajları kaldırıldı', + content_type=ContentType.APPLICATION_JSON.value) + + + def execute_conky(self, conky_message): + self.logger.debug("[CONKY] Executing conky.") + try: + if self.is_installed('conky') is False: + self.logger.info('[Conky] Could not found Conky. It will be installed') + self.logger.debug('[Conky] Conky installing with using apt-get') + self.install_with_apt_get('conky') + self.logger.info('[Conky] Could installed') + + self.logger.debug('[Conky] Some processes found which names are conky. They will be killed.') + self.execute('killall conky') + + except: + self.logger.error('[Conky] Conky install-kill problem.') + raise + + if self.is_exist(self.conky_config_file_dir) == False: + self.logger.debug('[Conky] Creating directory for conky config at ' + self.conky_config_file_dir) + self.create_directory(self.conky_config_file_dir) + + if self.is_exist(self.conky_config_file_path) == True: + self.logger.debug('[Conky] Old config file will be renamed.') + self.rename_file(self.conky_config_file_path, self.conky_config_file_path + '_old') + self.logger.debug('[Conky] Old config file will be renamed to ' + (self.conky_config_file_path + 'old')) + + self.create_file(self.conky_config_file_path) + self.write_file(self.conky_config_file_path, conky_message) + self.logger.debug('[Conky] Config file was filled by context.') + + + # creating wrapper file if is not exist. wrapper for using conky command..its need for ETA + if self.is_exist(self.conky_wrapper_file) == False: + self.logger.debug('[Conky] Creating directory for conky wrapper file at ' + self.conky_wrapper_file) + self.create_file(self.conky_wrapper_file) + self.write_file(self.conky_wrapper_file,self.conky_wrapper_content) + + if self.is_exist(self.conky_wrapper_file) == True: + self.execute('chmod +x ' + self.conky_wrapper_file) + + # creating autorun file if is not exist + if self.is_exist(self.conky_config_global_autorun_file) == False: + self.logger.debug('[Conky] Creating directory for conky autorun file at ' + self.conky_config_global_autorun_file) + self.create_file(self.conky_config_global_autorun_file) + self.write_file(self.conky_config_global_autorun_file, self.conky_autorun_content) + + users=self.Sessions.user_name() + + for user in users: + user_display = self.Sessions.display(user) + if user_display is None: + self.logger.debug('[Conky] executing for display none for user '+ str(user)) + self.execute('conky -q', result=False) + else : + self.logger.debug('[Conky] user display ' + str(user_display) +' user '+ str(user)) + conky_cmd= 'su ' + str(user) + ' -c ' + ' "conky --display=' + str(user_display) + ' " ' + self.logger.debug('[Conky] executing command: ' + str(conky_cmd)) + self.execute(conky_cmd, result=False) + + + #self.execute('conky ', result=False) + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Conky başarıyla oluşturuldu.', + data=json.dumps({'Result': conky_message}), + content_type=ContentType.APPLICATION_JSON.value) + + def handle_task(self): + try: + conky_message = self.data['conkyMessage'] + remove_conky_message = self.data['removeConkyMessage'] + + if remove_conky_message: + self.remove_conky_message() + + else: + self.execute_conky(conky_message) + + except Exception as e: + self.logger.error(" error on handle conky task. Error: " + str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Conky mesajı olusturulurken hata oluştu:' + str(e), + content_type=ContentType.APPLICATION_JSON.value) + + +def handle_task(task, context): + cls = RunConkyCommand(task, context) + cls.handle_task() diff --git a/src/plugins/conky/execute_xmessage.py b/src/plugins/conky/execute_xmessage.py new file mode 100644 index 0000000..12708c4 --- /dev/null +++ b/src/plugins/conky/execute_xmessage.py @@ -0,0 +1,135 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Edip YILDIZ +# Author: Tuncay ÇOLAK + +from base.model.enum.content_type import ContentType +import json +from base.plugin.abstract_plugin import AbstractPlugin +import threading + + +class RunXMessageCommand(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + # self.xmessage_command= "su {0} -c 'export DISPLAY={1} && export XAUTHORITY=~{2}/.Xauthority && xmessage \"{3}\" ' " + + self.custom_message_command = "su {0} -c 'export DISPLAY={1} && export XAUTHORITY=~{2}/.Xauthority && python3 /usr/share/ahenk/plugins/conky/ask.py \"LİDER AHENK BİLDİRİ\" \"{3}\" ' " + + # command for ltsp + self.custom_message_command_ltsp = "su {0} -c 'export DISPLAY={1} && export XAUTHORITY=~{2}/.Xauthority && python3 /usr/share/ahenk/plugins/conky/ask.py \"LİDER AHENK\\\ BİLDİRİ \" \"{3}\" ' " + + def execute_xmessage(self, message): + + users = self.Sessions.user_name(); + self.logger.debug('[XMessage] users : ' + str(users)) + + for user in users: + user_display = self.Sessions.display(user) + user_ip = self.Sessions.userip(user) + + if user_display is None: + self.logger.debug('[XMessage] executing for display none for user ' + str(user)) + + else: + self.logger.debug('[XMessage] user display ' + str(user_display) + ' user ' + str(user)) + + if user_ip is None: + t = threading.Thread( + target=self.execute(self.custom_message_command.format(user, user_display, user, message))) + t.start() + + else: + # message format for ltsp + self.logger.debug('user_ip: ' + str(user_ip) + ' user_display: ' + str(user_display)) + message_list = [] + message_parser = message.split(" ") + self.logger.debug('running parser:--->> ' + str(message_parser)) + for msg in message_parser: + message = '\\\ ' + str(msg) + message_list.append(message) + self.logger.debug('message_list:--->> ' + str(message_list)) + message = ''.join(str(x) for x in message_list) + self.logger.debug('message: ' + str(message)) + t = threading.Thread( + target=self.execute(self.custom_message_command_ltsp.format(user, user_display, user, message), + ip=user_ip)) + t.start() + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='İşlem başarıyla gerçekleştirildi.', + data=json.dumps({'Result': message}), + content_type=ContentType.APPLICATION_JSON.value) + + def execute_user_message(self, selected_user, message): + + users = self.Sessions.user_name(); + self.logger.debug('[XMessage] users : ' + str(users)) + + for user in users: + if selected_user in user: + user_display = self.Sessions.display(user) + user_ip = self.Sessions.userip(user) + + if user_display is None: + self.logger.debug('[XMessage] executing for display none for user ' + str(user)) + + else: + self.logger.debug('[XMessage] user display ' + str(user_display) + ' user ' + str(user)) + + if user_ip is None: + t = threading.Thread(target=self.execute( + self.custom_message_command.format(user, user_display, user, message))) + t.start() + + #message format for ltsp + else: + self.logger.debug('user_ip: ' + str(user_ip) + ' user_display: ' + str(user_display)) + message_list = [] + message_parser = message.split(" ") + self.logger.debug('running parser:--->> ' + str(message_parser)) + for msg in message_parser: + message = '\\\ ' + str(msg) + message_list.append(message) + self.logger.debug('message_list:--->> ' + str(message_list)) + message = ''.join(str(x) for x in message_list) + self.logger.debug('message: ' + str(message)) + t = threading.Thread(target=self.execute( + self.custom_message_command_ltsp.format(user, user_display, user, message), ip=user_ip)) + t.start() + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='İşlem başarıyla gerçekleştirildi.', + data=json.dumps({'Result': message}), + content_type=ContentType.APPLICATION_JSON.value) + + + def handle_task(self): + try: + message = self.data['message'] + self.logger.debug('[XMessage]: get message from lider: ' + str(message)) + selected_user = None + + if 'selected_user' in self.data: + selected_user = str(self.data['selected_user']) + self.logger.debug('[XMessage]: selected User: ' + str(selected_user)) + self.execute_user_message(selected_user, message) + + else: + self.execute_xmessage(message) + + except Exception as e: + self.logger.error(" error on handle xmessage task. Error: " + str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='XMessage mesajı olusturulurken hata oluştu:' + str(e), + content_type=ContentType.APPLICATION_JSON.value) + + +def handle_task(task, context): + cls = RunXMessageCommand(task, context) + cls.handle_task() diff --git a/src/plugins/conky/main.py b/src/plugins/conky/main.py new file mode 100644 index 0000000..b1cfb32 --- /dev/null +++ b/src/plugins/conky/main.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +def info(): + inf = dict() + inf['name'] = 'conky' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = '' + inf['task'] = True + inf['user_oriented'] = True + inf['machine_oriented'] = True + inf['developer'] = 'bm.volkansahin@gmail.com' + + return inf diff --git a/src/plugins/conky/policy.py b/src/plugins/conky/policy.py new file mode 100644 index 0000000..fad11ac --- /dev/null +++ b/src/plugins/conky/policy.py @@ -0,0 +1,155 @@ +# !/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Conky(AbstractPlugin): + def __init__(self, data, context): + super(Conky, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.machine_profile = True + self.conky_config_file_dir = '/etc/conky/' + self.conky_config_file_path = '/etc/conky/conky.conf' + self.command_autorun_conky = 'sleep 3;conky -d {0} -c {1}' + self.username = None + self.autostart_dir_path = '{0}.config/autostart/' + self.autorun_file_path = '{0}conky.desktop' + self.logger.debug('Parameters were initialized.') + + def handle_policy(self): + try: + + # Checking dependecies + if self.check_dependencies(['conky', 'conky-all']) is True: + self.logger.debug('Dependencies checked.') + else: + return + + # Killing conky processes + self.logger.debug('Conky named processes will be killed.') + self.execute('killall -9 conky') + + # Is user profile + if 'username' in self.context.data and self.context.get('username') is not None: + self.logger.debug('This is user profile, parameters reinitializing.') + self.username = self.context.get('username') + self.conky_config_file_dir = '{0}.conky/'.format(self.Sessions.user_home_path(self.username)) + self.conky_config_file_path = '{0}conky.conf'.format(self.conky_config_file_dir) + self.machine_profile = False + + # Creating/checking conky file dir and conky conf file + self.logger.debug('Conky file directory and configuration file is creating/checking') + if self.is_exist(self.conky_config_file_dir): + self.logger.debug('Old config file will be deleted.') + self.delete_file(self.conky_config_file_path) + else: + self.logger.debug( + 'Creating directory for conky config at {0}'.format(self.conky_config_file_dir)) + self.create_directory(self.conky_config_file_dir) + + if self.create_file(self.conky_config_file_path): + self.logger.debug('Config file was created.') + self.write_file(self.conky_config_file_path, json.loads(self.data)['message']) + self.logger.debug('Config file was filled by context.') + else: + self.logger.error('A problen occurred while creating Conky configuration file.') + raise Exception('File {0} could not created.'.format(self.conky_config_file_path)) + + # Creating autorun + self.logger.debug('Creating autorun file...') + self.initialize_auto_run() + + if self.machine_profile is False: + self.execute( + self.command_autorun_conky.format('--display=' + self.Sessions.display(self.username), + self.conky_config_file_path), + as_user=self.username, result=False) + + self.execute('chown -hR ' + self.username + ':' + self.username + ' ' + self.conky_config_file_dir) + self.logger.debug('Owner of Conky config file was changed.') + else: + self.execute(self.command_autorun_conky.format('', self.conky_config_file_path), result=False) + + self.logger.debug('Autorun command executed successfully') + self.context.create_response(code=self.get_message_code().POLICY_PROCESSED.value, + message='Conky politikası başarıyla çalıştırıldı.') + + except Exception as e: + self.logger.error( + 'A problem occurred while handling Conky policy. Error Message: {}'.format(str(e))) + self.context.create_response(code=self.get_message_code().POLICY_ERROR.value, + message='Conky politikası uygulanırken bir hata oluştu.') + + def check_dependencies(self, packages): + + self.logger.debug('Checking dependencies') + for package in packages: + if self.is_installed(package) is False: + self.logger.debug('Could not found {0}. It will be installed'.format(package)) + result_code, p_out, p_err = self.install_with_apt_get(package) + if result_code == 0: + self.logger.debug('{0} installed successfully'.format(package)) + else: + self.logger.error( + 'A problem occurred while installing {0} package. Error Message: {1}'.format(package, + str( + p_err))) + self.context.create_response(code=self.get_message_code().POLICY_ERROR.value, + message='Bağımlılıklardan {0} paketi kurulurken hata oluştu.') + return False + + return True + + def initialize_auto_run(self): + + if self.machine_profile is True: + self.logger.debug('All users conky configuration files will be removed because of machine profile') + if self.Sessions.user_name() is not None and len(self.Sessions.user_name()) > 0: + for username in self.Sessions.user_name(): + self.logger.debug( + 'Removing conf file of user {0}'.format(username)) + self.delete_file( + self.autorun_file_path.format( + self.autostart_dir_path.format(self.Sessions.user_home_path(username)))) + else: + self.logger.debug( + 'There are no user') + + else: + home_path = self.Sessions.user_home_path(self.username) + self.logger.debug( + 'Creating autorun file for user {0}'.format(self.username)) + self.create_autorun_file(self.autostart_dir_path.format(home_path), + self.conky_config_file_path, + self.autorun_file_path.format(self.autostart_dir_path.format(home_path))) + self.logger.debug( + 'Autorun created') + + def create_autorun_file(self, autostart_path, conky_config_file_path, autorun_file_path): + if not self.is_exist(autostart_path): + self.logger.debug( + 'Creating file: {0}'.format(autostart_path)) + self.create_directory(autostart_path) + + file_content = '[Desktop Entry]\n' \ + 'Encoding=UTF-8 \n' \ + 'Type=Application \n' \ + 'Name=Conky \n' \ + 'Comment=Conky Monitor \n' \ + 'Exec=conky -d -c ' + conky_config_file_path + '\n' \ + 'StartupNotify=false \n' \ + 'Terminal=false \n' + self.logger.debug( + 'Writing content to autorun file.') + self.write_file(autorun_file_path, file_content, 'w') + + +def handle_policy(profile_data, context): + plugin = Conky(profile_data, context) + plugin.handle_policy() diff --git a/src/plugins/disk-quota/api/disk_quota.py b/src/plugins/disk-quota/api/disk_quota.py new file mode 100644 index 0000000..94fc37a --- /dev/null +++ b/src/plugins/disk-quota/api/disk_quota.py @@ -0,0 +1,119 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import json +import os +import sys + +from base.plugin.abstract_plugin import AbstractPlugin + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__)))) + +from fstab import Fstab + + +class DiskQuota(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + self.username = self.context.get('username') + + self.mount = 'mount -o remount /home' + self.quotacheck = 'quotacheck -cfmvF vfsv0 /home' + self.quotaon_all = 'quotaon --all' + self.quotaon_avug = 'quotaon -avug' + self.set_quota = 'setquota -u {0} {1} {2} 0 0 /home' + self.get_quota = 'quota -u {0} | awk \'{{print $4}}\' | tail -1' + + self.parameters = json.loads(self.data) + + self.soft_quota = str(int(self.parameters['soft-quota']) * 1024) + self.hard_quota = str(int(self.parameters['hard-quota']) * 1024) + self.default_quota = str(int(self.parameters['default-quota']) * 1024) + + self.old_quota = None + + self.logger.debug('Parameters were initialized.') + + def handle_policy(self): + self.logger.debug('Policy handling...') + try: + + if 'username' in self.context.data and self.context.get('username') is not None: + self.logger.debug('This is user profile, parameters reinitializing.') + self.username = self.context.get('username') + + self.old_quota = self.execute(self.get_quota.format(self.username))[1] + # Check fstab & append 'usrquota' option if not exists + # fs = Fstab() + # fs.read('/etc/fstab') + # fstab_entries = [] + # fslines = fs.lines + # for line in fslines: + # if line.has_filesystem() and 'usrquota' not in line.options: + # if line.dict['directory'] == '/' or line.dict['directory'] == '/home/': + # self.logger.debug('Appending \'usrquota\' option to {}'.format(line.dict['directory'])) + # line.options += ['usrquota'] + # fstab_entries.append(line.dict['directory']) + # fs.write('/etc/fstab') + + # Re-mount necessary fstab entries + # for entry in fstab_entries: + # self.execute(self.mount.format(entry)) + # self.logger.debug('Remounting fstab entry {}'.format(entry)) + self.execute(self.quotacheck) + self.logger.debug('{}'.format(self.quotacheck)) + + self.execute(self.quotaon_all) + self.logger.debug('{}'.format(self.quotaon_all)) + + self.execute(self.quotaon_avug) + self.logger.debug('{}'.format(self.quotaon_avug)) + + self.execute(self.set_quota.format(self.username, self.soft_quota, self.hard_quota)) + self.logger.debug( + 'Set soft and hard quota. Username: {0}, Soft Quota: {1}, Hard Quota: {2}'.format(self.username, + self.soft_quota, + self.hard_quota)) + + self.create_default_quota_file() + + result = dict() + if self.context.is_mail_send(): + mail_content = self.context.get_mail_content() + if mail_content.__contains__('{ahenk-ip}'): + mail_content = str(mail_content).replace('{ahenk-ip}', ' {0} IP\'li Ahenk\'teki yeni'.format( + str(self.Hardware.ip_addresses()))) + if mail_content.__contains__('{old-quota}'): + mail_content = str(mail_content).replace('{old-quota}', + ' Eski kota değeri {0} MB olan'.format( + str(int(self.old_quota) / 1024))) + if mail_content.__contains__('{soft-quota}'): + mail_content = str(mail_content).replace('{soft-quota}', str(int(self.soft_quota) / 1024) + ' MB') + if mail_content.__contains__('{hard-quota}'): + mail_content = str(mail_content).replace('{hard-quota}', str(int(self.hard_quota) / 1024) + ' MB') + if mail_content.__contains__('{default-quota}'): + mail_content = str(mail_content).replace('{default-quota}', + str(int(self.default_quota)/1024) + ' MB') + + self.context.set_mail_content(mail_content) + result['mail_content'] = str(self.context.get_mail_content()) + result['mail_subject'] = str(self.context.get_mail_subject()) + result['mail_send'] = self.context.is_mail_send() + + self.context.create_response(code=self.get_message_code().POLICY_PROCESSED.value, + data=json.dumps(result), + message='Kotalar başarıyla güncellendi.', + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.error('[DiskQuota] A problem occurred while handling browser profile: {0}'.format(str(e))) + self.context.create_response(code=self.get_message_code().POLICY_ERROR.value, + message='Disk Quota profili uygulanırken bir hata oluştu.') + + def create_default_quota_file(self): + self.write_file('default_quota', self.default_quota) diff --git a/src/plugins/disk-quota/api/disk_quota_ltsp.py b/src/plugins/disk-quota/api/disk_quota_ltsp.py new file mode 100644 index 0000000..101c654 --- /dev/null +++ b/src/plugins/disk-quota/api/disk_quota_ltsp.py @@ -0,0 +1,91 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import json +import os +import sys + +from base.plugin.abstract_plugin import AbstractPlugin + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__)))) + + + +class DiskQuota(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.username = self.context.get('username') + + self.mount = 'mount -o remount {}' + self.quotaon_all = 'quotaon --all' + self.quotaon_avug = 'quotaon -avug' + #self.set_quota = 'setquota --always-resolve -u {0} {1} {2} 0 0 --all' + self.set_quota = 'quotatool -u {0} -b -q {1} -l {2} /home' + #self.get_quota = 'quota -u {0} | awk \'{{print $4}}\' | tail -1' + self.get_quota = 'repquota /home | grep {0} | awk \'{{print $5}}\'' + + self.parameters = json.loads(self.data) + + self.soft_quota = str(int(self.parameters['soft-quota']) * 1024) + self.hard_quota = str(int(self.parameters['hard-quota']) * 1024) + self.default_quota = str(int(self.parameters['default-quota']) * 1024) + + self.old_quota = None + + self.logger.debug('Parameters were initialized.') + + def handle_policy(self): + self.logger.debug('Policy handling...') + try: + self.username = self.context.get('username') + + + self.old_quota = self.execute(self.get_quota.format(self.username))[1] + + self.execute(self.set_quota.format(self.username, self.soft_quota, self.hard_quota)) + self.logger.debug( + 'Set soft and hard quota. Username: {0}, Soft Quota: {1}, Hard Quota: {2}'.format(self.username, + self.soft_quota, + self.hard_quota)) + + self.create_default_quota_file() + + result = dict() + if self.context.is_mail_send(): + mail_content = self.context.get_mail_content() + if mail_content.__contains__('{ahenk-ip}'): + mail_content = str(mail_content).replace('{ahenk-ip}', ' {0} IP\'li Ahenk\'teki yeni'.format( + str(self.Hardware.ip_addresses()))) + if mail_content.__contains__('{old-quota}'): + mail_content = str(mail_content).replace('{old-quota}', + ' Mevcut kota değeri {0} MB olan'.format(str(int(self.old_quota)/1024))) + if mail_content.__contains__('{soft-quota}'): + mail_content = str(mail_content).replace('{soft-quota}',str(int(self.soft_quota)/1024)+' MB') + if mail_content.__contains__('{hard-quota}'): + mail_content = str(mail_content).replace('{hard-quota}', str(int(self.hard_quota)/1024)+' MB') + if mail_content.__contains__('{default-quota}'): + mail_content = str(mail_content).replace('{default-quota}', str(int(self.default_quota)/1024)+' MB') + + self.context.set_mail_content(mail_content) + result['mail_content'] = str(self.context.get_mail_content()) + result['mail_subject'] = str(self.context.get_mail_subject()) + result['mail_send'] = self.context.is_mail_send() + + self.context.create_response(code=self.get_message_code().POLICY_PROCESSED.value, + data=json.dumps(result), + message='Kotalar başarıyla güncellendi.', + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.error('[DiskQuota] A problem occurred while handling browser profile: {0}'.format(str(e))) + self.context.create_response(code=self.get_message_code().POLICY_ERROR.value, + message='Disk Quota profili uygulanırken bir hata oluştu.') + + def create_default_quota_file(self): + self.write_file('default_quota', self.default_quota) diff --git a/src/plugins/disk-quota/fstab.py b/src/plugins/disk-quota/fstab.py new file mode 100644 index 0000000..41dc6fa --- /dev/null +++ b/src/plugins/disk-quota/fstab.py @@ -0,0 +1,86 @@ +import os +import sys +import tempfile + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__)))) + +from line import Line + + +class Fstab(object): + """An /etc/fstab file.""" + + def __init__(self): + self.lines = [] + + def open_file(self, filespec, mode): + if isinstance(filespec, str): + return open(filespec, mode=mode) + else: + return filespec + + def close_file(self, f, filespec): + if isinstance(filespec, str): + f.close() + + def get_perms(self, filename): + return os.stat(filename).st_mode # pragma: no cover + + def chmod_file(self, filename, mode): + os.chmod(filename, mode) # pragma: no cover + + def link_file(self, oldname, newname): + if os.path.exists(newname): + os.remove(newname) + os.link(oldname, newname) + + def rename_file(self, oldname, newname): + os.rename(oldname, newname) # pragma: no cover + + def read(self, filespec): + """Read in a new file. + + If filespec is a string, it is used as a filename. Otherwise + it is used as an open file. + + The existing content is replaced. + + """ + + f = self.open_file(filespec, "r") + lines = [] + for line in f: + lines.append(Line(line)) + self.lines = lines + self.close_file(filespec, f) + + def write(self, filespec): + """Write out a new file. + + If filespec is a string, it is used as a filename. Otherwise + it is used as an open file. + + """ + + if isinstance(filespec, str): + # We create the temporary file in the directory (/etc) that the + # file exists in. This is so that we can do an atomic rename + # later, and that only works inside one filesystem. Some systems + # have /tmp and /etc on different filesystems, for good reasons, + # and we need to support that. + dirname = os.path.dirname(filespec) + prefix = os.path.basename(filespec) + "." + fd, tempname = tempfile.mkstemp(dir=dirname, prefix=prefix) + os.close(fd) + else: + tempname = filespec + + f = self.open_file(tempname, "w") + for line in self.lines: + f.write(line.raw) + self.close_file(filespec, f) + + if isinstance(filespec, str): + self.chmod_file(tempname, self.get_perms(filespec)) + self.link_file(filespec, filespec + ".bak") + self.rename_file(tempname, filespec) diff --git a/src/plugins/disk-quota/get_quota.py b/src/plugins/disk-quota/get_quota.py new file mode 100644 index 0000000..96a50f5 --- /dev/null +++ b/src/plugins/disk-quota/get_quota.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class GetQuota(AbstractPlugin): + def __init__(self, task, context): + super(AbstractPlugin, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.get_quota = 'repquota /home | tail -n +6 | awk \'{print $1,$4,$5,$3}\'' + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + + result_code, p_out, p_err = self.execute(self.get_quota) + + user_list = [] + lines = str(p_out).split('\n') + + for line in lines: + detail = line.split(' ') + + if str(detail[0]).strip() is not None and str(detail[0]).strip() != '': + user = {'user': str(detail[0]).strip(), 'soft_quota': str(detail[1]).strip(), + 'hard_quota': str(detail[2]).strip(), 'disk_usage': str(detail[3]).strip()} + user_list.append(user) + + self.logger.debug( + 'user: {0}, soft_quota: {1}, hard_quota: {2}, disk_usage: {3}' + .format(str(detail[0]).strip(), str(detail[1]).strip(), str(detail[2]).strip(), + str(detail[3]).strip())) + + self.logger.info('DISK-QUOTA task is handled successfully') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Kota bilgileri başarıyla alındı.', + data=json.dumps({'users': user_list}), + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.error('A problem occured while handling DISK-QUOTA task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='DISK-QUOTA görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + gq = GetQuota(task, context) + gq.handle_task() diff --git a/src/plugins/disk-quota/init.py b/src/plugins/disk-quota/init.py new file mode 100644 index 0000000..f78d92f --- /dev/null +++ b/src/plugins/disk-quota/init.py @@ -0,0 +1,24 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Init(AbstractPlugin): + def __init__(self, context): + super(Init, self).__init__() + self.context = context + self.logger = self.get_logger() + + self.logger.debug('Parameters were initialized.') + + def handle_init_mode(self): + if self.is_installed('quota') is False: + self.logger.debug('Installing quota with apt-get...') + self.install_with_apt_get('quota') + + +def handle_mode(context): + init = Init(context) + init.handle_init_mode() diff --git a/src/plugins/disk-quota/line.py b/src/plugins/disk-quota/line.py new file mode 100644 index 0000000..9221325 --- /dev/null +++ b/src/plugins/disk-quota/line.py @@ -0,0 +1,104 @@ +import re + + +class Line(object): + """A line in an /etc/fstab line. + + Lines may or may not have a filesystem specification in them. The + has_filesystem method tells the user whether they do or not; if they + do, the attributes device, directory, fstype, options, dump, and + fsck contain the values of the corresponding fields, as instances of + the sub-classes of the LinePart class. For non-filesystem lines, + the attributes have the None value. + + Lines may or may not be syntactically correct. If they are not, + they are treated as as non-filesystem lines. + + """ + + # Lines split this way to shut up coverage.py. + attrs = ("ws1", "device", "ws2", "directory", "ws3", "fstype") + attrs += ("ws4", "options", "ws5", "dump", "ws6", "fsck", "ws7") + + def __init__(self, raw): + self.dict = {} + self.raw = raw + + def __getattr__(self, name): + if name in self.dict: + return self.dict[name] + else: + raise AttributeError(name) + + def __setattr__(self, name, value): + forbidden = ("dict", "dump", "fsck", "options") + if name not in forbidden and name in self.dict: + if self.dict[name] is None: + raise Exception("Cannot set attribute %s when line dies not " + "contain filesystem specification" % name) + self.dict[name] = value + else: + object.__setattr__(self, name, value) + + def get_dump(self): + return int(self.dict["dump"]) + + def set_dump(self, value): + self.dict["dump"] = str(value) + + dump = property(get_dump, set_dump) + + def get_fsck(self): + return int(self.dict["fsck"]) + + def set_fsck(self, value): + self.dict["fsck"] = str(value) + + fsck = property(get_fsck, set_fsck) + + def get_options(self): + return self.dict["options"].split(",") + + def set_options(self, list): + self.dict["options"] = ",".join(list) + + options = property(get_options, set_options) + + def set_raw(self, raw): + match = False + + if raw.strip() != "" and not raw.strip().startswith("#"): + pat = r"^(?P\s*)" + pat += r"(?P\S*)" + pat += r"(?P\s+)" + pat += r"(?P\S+)" + pat += r"(?P\s+)" + pat += r"(?P\S+)" + pat += r"(?P\s+)" + pat += r"(?P\S+)" + pat += r"(?P\s+)" + pat += r"(?P\d+)" + pat += r"(?P\s+)" + pat += r"(?P\d+)" + pat += r"(?P\s*)$" + + match = re.match(pat, raw) + if match: + self.dict.update((attr, match.group(attr)) for attr in self.attrs) + + if not match: + self.dict.update((attr, None) for attr in self.attrs) + + self.dict["raw"] = raw + + def get_raw(self): + if self.has_filesystem(): + return "".join(self.dict[attr] for attr in self.attrs) + else: + return self.dict["raw"] + + raw = property(get_raw, set_raw) + + def has_filesystem(self): + """Does this line have a filesystem specification?""" + return self.device is not None diff --git a/src/plugins/disk-quota/main.py b/src/plugins/disk-quota/main.py new file mode 100644 index 0000000..e73a873 --- /dev/null +++ b/src/plugins/disk-quota/main.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +def info(): + inf = dict() + inf['name'] = 'disk-quota' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'Disk-Quota plugin provides to get current soft - hard quota and changing them.' + inf['task'] = True + inf['user_oriented'] = True + inf['machine_oriented'] = False + inf['developer'] = 'mine.dogan@agem.com.tr' + + return inf \ No newline at end of file diff --git a/src/plugins/disk-quota/policy.py b/src/plugins/disk-quota/policy.py new file mode 100644 index 0000000..bf92007 --- /dev/null +++ b/src/plugins/disk-quota/policy.py @@ -0,0 +1,18 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import json +import os +import sys + +from base.plugin.abstract_plugin import AbstractPlugin + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__)))) + +from fstab import Fstab +from api.disk_quota import DiskQuota + +def handle_policy(profile_data, context): + dq = DiskQuota(profile_data, context) + dq.handle_policy() diff --git a/src/plugins/disk-quota/safe.py b/src/plugins/disk-quota/safe.py new file mode 100644 index 0000000..04f9a6c --- /dev/null +++ b/src/plugins/disk-quota/safe.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +import os +import sys +from base.plugin.abstract_plugin import AbstractPlugin + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__)))) + +from fstab import Fstab + + +class Safe(AbstractPlugin): + def __init__(self, context): + super(Safe, self).__init__() + self.context = context + self.username = str(context.get_username()) + self.mount = 'mount -o remount /home' + self.quotacheck = 'quotacheck -cfmvF vfsv0 /home' + self.quotaon_all = 'quotaon --all' + self.quotaon_avug = 'quotaon -avug' + self.set_quota = 'setquota -u {0} {1} {2} 0 0 /home' + self.logger = self.get_logger() + + def handle_safe_mode(self): + if self.is_exist('default_quota'): + quota_size = self.read_file('default_quota') + + try: + # Check fstab & append 'usrquota' option if not exists + #fs = Fstab() + #fs.read('/etc/fstab') + #fstab_entries = [] + #fslines = fs.lines + #for line in fslines:' + # if line.has_filesystem() and 'usrquota' not in line.options: + # if line.dict['directory'] == '/' or line.dict['directory'] == '/home/': + # self.logger.debug('Appending \'usrquota\' option to {}'.format(line.dict['directory'])) + # line.options += ['usrquota'] + # fstab_entries.append(line.dict['directory']) + #fs.write('/etc/fstab')# + + # Re-mount necessary fstab entries + #for entry in fstab_entries: + # self.execute(self.mount.format(entry)) + # self.logger.debug('Remounting fstab entry {}'.format(entry)) + + self.execute(self.quotacheck) + self.logger.debug('{}'.format(self.quotacheck)) + + self.execute(self.quotaon_all) + self.logger.debug('{}'.format(self.quotaon_all)) + + self.execute(self.quotaon_avug) + self.logger.debug('{}'.format(self.quotaon_avug)) + + self.execute(self.set_quota.format(self.username, quota_size, quota_size)) + self.logger.debug( + 'Set soft and hard quota. Username: {0}, Soft Quota: {1}, Hard Quota: {2}'.format(self.username,quota_size,quota_size)) + + + except Exception as e: + self.logger.error('[DiskQuota] A problem occurred while handling browser profile: {0}'.format(str(e))) + + +def handle_mode(context): + safe = Safe(context) + safe.handle_safe_mode() \ No newline at end of file diff --git a/src/plugins/file-management/get_file_content.py b/src/plugins/file-management/get_file_content.py new file mode 100644 index 0000000..eaf251a --- /dev/null +++ b/src/plugins/file-management/get_file_content.py @@ -0,0 +1,43 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +from base.plugin.abstract_plugin import AbstractPlugin +import json + + +class GetFileContent(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + def handle_task(self): + try: + file_path = self.data['file-path'] + file_content = "" + is_file_exists = False + + if self.is_exist(file_path): + self.logger.info("File exists: " + file_path) + is_file_exists = True + file_content = self.read_file(file_path) + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Dosya içeriği başarıyla alındı..', + data=json.dumps({'file_exists': is_file_exists, 'file_content': file_content}), + content_type=self.get_content_type().APPLICATION_JSON.value) + else: + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Dosya bulunamadı..', + content_type=self.get_content_type().APPLICATION_JSON.value) + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Dosya içeriği alınırken hata oluştu: {0}'.format(str(e))) + + +def handle_task(task, context): + plugin = GetFileContent(task, context) + plugin.handle_task() diff --git a/src/plugins/file-management/main.py b/src/plugins/file-management/main.py new file mode 100644 index 0000000..307782f --- /dev/null +++ b/src/plugins/file-management/main.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +def info(): + inf = dict() + inf['name'] = 'file-management' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = '' + inf['task'] = True + inf['user_oriented'] = False + inf['machine_oriented'] = False + inf['developer'] = '' + + return inf diff --git a/src/plugins/file-management/write_to_file.py b/src/plugins/file-management/write_to_file.py new file mode 100644 index 0000000..041f6a0 --- /dev/null +++ b/src/plugins/file-management/write_to_file.py @@ -0,0 +1,46 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +from base.plugin.abstract_plugin import AbstractPlugin + +class WriteToFile(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + def handle_task(self): + try: + file_path = self.data['file-path'] + file_content = self.data['file-content'] + + if self.is_exist(file_path): + self.write_file(file_path, file_content) + else: + path_str = "" + for idx, folder in enumerate(file_path.split("/")): + if idx != len(file_path.split("/")) - 1: + path_str += folder + "/" + (result_code, p_out, p_err) = self.execute("mkdir -p /" + path_str) + + if result_code == 0: + self.logger.error('Folders are created') + else: + self.logger.error('Error occured while creating folders.') + self.write_file(file_path, file_content) + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='İçerik dosyaya başarıyla yazıldı..', + content_type=self.get_content_type().APPLICATION_JSON.value) + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='İçerik dosyaya yazılırken hata oluştu: {0}'.format(str(e))) + + +def handle_task(task, context): + plugin = WriteToFile(task, context) + plugin.handle_task() diff --git a/src/plugins/ldap-login/execute_ad_login.py b/src/plugins/ldap-login/execute_ad_login.py new file mode 100644 index 0000000..fa34abc --- /dev/null +++ b/src/plugins/ldap-login/execute_ad_login.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Tuncay ÇOLAK + +# Active Directory authentication task + +import configparser +from base.plugin.abstract_plugin import AbstractPlugin +from base.registration.execute_sssd_ad_authentication import ExecuteSSSDAdAuthentication +from base.registration.registration import Registration + +class ADLogin(AbstractPlugin): + + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + self.ad_authentication = ExecuteSSSDAdAuthentication() + self.registration = Registration() + self.config = configparser.ConfigParser() + self.ahenk_conf_path = "/etc/ahenk/ahenk.conf" + + def handle_task(self): + try: + domain_name = self.data['domain_name'] + hostname = self.data['hostname'] + ip_address = self.data['ip_address'] + ad_username = self.data['ad_username'] + admin_password = self.data['admin_password'] + ad_port = self.data['ad_port'] + disabled_local_user = self.data['disableLocalUser'] + + execution_result = self.ad_authentication.authenticate(domain_name, hostname, ip_address, admin_password, ad_username) + if execution_result is False: + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Active Directory kullanıcısı ile oturum açma ayarlanırken hata oluştu.: Gerekli Paketleri indirilemedi.', + content_type=self.get_content_type().APPLICATION_JSON.value) + else: + # if get disabled_local_user TRUE set user_disabled in ahenk.conf. disabled local users then client reboot + self.config.read(self.ahenk_conf_path) + if disabled_local_user is True: + # self.registration.disable_local_users() + config = configparser.ConfigParser() + config.read(self.ahenk_conf_path) + config.set('MACHINE', 'user_disabled', 'true') + + with open(self.ahenk_conf_path, 'w') as configfile: + self.logger.info('Opening config file ') + config.write(configfile) + configfile.close() + self.logger.info('User disabled value Disabled') + + else: + self.logger.info("local users will not be disabled because local_user parameter is FALSE") + self.shutdown() + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Active Directory kullanıcısı ile oturum açma başarı ile sağlandı ve istemci yeniden başlatılıyor.', + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Active Directory kullanıcısı ile oturum açma ayarlanırken hata oluştu.: {0}'.format(str(e))) + +def handle_task(task, context): + plugin = ADLogin(task, context) + plugin.handle_task() diff --git a/src/plugins/ldap-login/execute_cancel_ldap_login.py b/src/plugins/ldap-login/execute_cancel_ldap_login.py new file mode 100644 index 0000000..572f8cb --- /dev/null +++ b/src/plugins/ldap-login/execute_cancel_ldap_login.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Hasan Kara +# Author: Tuncay ÇOLAK + +# Cancel AD or OpenLDAP authentication task + +import configparser +from base.plugin.abstract_plugin import AbstractPlugin +from base.registration.execute_cancel_ldap_login import ExecuteCancelLDAPLogin +from base.registration.execute_cancel_sssd_authentication import ExecuteCancelSSSDAuthentication +from base.registration.execute_cancel_sssd_ad_authentication import ExecuteCancelSSSDAdAuthentication +from base.registration.registration import Registration + +class CancelLDAPLogin(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + self.ldap_login = ExecuteCancelLDAPLogin() + self.sssd_authentication = ExecuteCancelSSSDAuthentication() + self.ad_authentication = ExecuteCancelSSSDAdAuthentication() + self.registration = Registration() + self.config = configparser.ConfigParser() + self.ahenk_conf_path = "/etc/ahenk/ahenk.conf" + + def handle_task(self): + directory_type = "LDAP" + try: + if self.is_exist("/etc/ahenk/ad_info"): + directory_type = "AD" + if directory_type == "LDAP": + self.sssd_authentication.cancel() + else: + self.ad_authentication.cancel() + + self.config.read(self.ahenk_conf_path) + if self.config.has_section('MACHINE'): + user_disabled = self.config.get("MACHINE", "user_disabled") + self.logger.info('User disabled value:' + str(user_disabled)) + if user_disabled != 'false': + self.logger.info('Enable Users') + + self.registration.enable_local_users() + self.config.set('MACHINE', 'user_disabled', 'false') + + with open(self.ahenk_conf_path, 'w') as configfile: + self.logger.info('Opening config file ') + self.config.write(configfile) + self.logger.info('User disabled value FALSE') + configfile.close() + else: + self.logger.info('Local users already enabled') + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='{0} kullanıcısı ile oturum açabilme başarıyla iptal edildi.'.format(directory_type), + content_type=self.get_content_type().APPLICATION_JSON.value) + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='{0} kullanıcısı ile oturum açabilme iptal edilirken hata oluştu.: {1}'.format(directory_type, str(e))) + +def handle_task(task, context): + plugin = CancelLDAPLogin(task, context) + plugin.handle_task() diff --git a/src/plugins/ldap-login/execute_ldap_login.py b/src/plugins/ldap-login/execute_ldap_login.py new file mode 100644 index 0000000..6504e76 --- /dev/null +++ b/src/plugins/ldap-login/execute_ldap_login.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +import configparser +from base.plugin.abstract_plugin import AbstractPlugin +from base.registration.execute_ldap_login import ExecuteLDAPLogin +from base.registration.execute_sssd_authentication import ExecuteSSSDAuthentication +from base.registration.registration import Registration + +class LDAPLogin(AbstractPlugin): + + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + self.ldap_login = ExecuteLDAPLogin() + self.sssd_authentication = ExecuteSSSDAuthentication() + self.config = configparser.ConfigParser() + self.registration = Registration() + self.ahenk_conf_path = "/etc/ahenk/ahenk.conf" + + def handle_task(self): + try: + server_address = self.data['server-address'] + dn = self.data['dn'] + # version = self.data['version'] + admin_dn = self.data['admin-dn'] + admin_password = self.data['admin-password'] + disabled_local_user = self.data['disableLocalUser'] + + execution_result = self.sssd_authentication.authenticate(server_address, dn, admin_dn, admin_password) + if execution_result is False: + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='LDAP kullanıcısı ile oturum açma ayarlanırken hata oluştu.: SSSD Paketleri indirilemedi.', + content_type=self.get_content_type().APPLICATION_JSON.value) + else: + # if get disabled_local_user TRUE set user_disabled in ahenk.conf. disabled local users then client reboot + self.config.read(self.ahenk_conf_path) + if disabled_local_user is True: + # self.registration.disable_local_users() + config = configparser.ConfigParser() + config.read(self.ahenk_conf_path) + config.set('MACHINE', 'user_disabled', 'true') + + with open(self.ahenk_conf_path, 'w') as configfile: + self.logger.info('Opening config file ') + config.write(configfile) + configfile.close() + + self.logger.info('User disabled value Disabled') + else: + self.logger.info("local users will not be disabled because local_user parameter is FALSE") + self.shutdown() + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='LDAP kullanıcısı ile oturum açma başarı ile sağlandı ve istemci yeniden başlatılıyor.', + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='LDAP kullanıcısı ile oturum açma ayarlanırken hata oluştu.: {0}'.format(str(e))) + +def handle_task(task, context): + plugin = LDAPLogin(task, context) + plugin.handle_task() diff --git a/src/plugins/ldap-login/init.py b/src/plugins/ldap-login/init.py new file mode 100644 index 0000000..f8162b3 --- /dev/null +++ b/src/plugins/ldap-login/init.py @@ -0,0 +1,20 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Init(AbstractPlugin): + def __init__(self, context): + super(Init, self).__init__() + self.context = context + self.logger = self.get_logger() + + def handle_mode(self): + # TODO Do what do you want to do! + pass + + +def handle_mode(context): + init = Init(context) + init.handle_mode() diff --git a/src/plugins/ldap-login/main.py b/src/plugins/ldap-login/main.py new file mode 100644 index 0000000..f280671 --- /dev/null +++ b/src/plugins/ldap-login/main.py @@ -0,0 +1,15 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +def info(): + inf = dict() + inf['name'] = 'ldap-login' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'LDAP user authentication ' + inf['task'] = True + inf['user_oriented'] = False + inf['machine_oriented'] = False + inf['developer'] = 'h.kara27@gmail.com' + + return inf \ No newline at end of file diff --git a/src/plugins/ldap/delete_agent.py b/src/plugins/ldap/delete_agent.py new file mode 100644 index 0000000..89085d1 --- /dev/null +++ b/src/plugins/ldap/delete_agent.py @@ -0,0 +1,66 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Edip YILDIZ +# Author: Tuncay ÇOLAK + + +from base.model.enum.content_type import ContentType +import json, threading + + +from base.plugin.abstract_plugin import AbstractPlugin + +import threading + + +class MoveAgent(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + + + def update_dn(self, jid, newDn): + cols = ['dn']; + values = [newDn] + return self.db_service.update('registration', cols, values, 'jid=\''+jid+'\'') + + + + def getCnFromDn(self,dn): + if dn !=None and str(dn) !="": + dnStrArr = str(dn).split(",") + if len(dnStrArr)>0: + return dnStrArr[0] + + + def handle_task(self): + try: + dn = self.data['dn'] + newParentDn = self.data['newParentDn'] + + jid= self.db_service.select_one_result('registration','jid','registered = 1') + + newDn=str(dn).replace(dn, self.getCnFromDn(dn)+ str(newParentDn)) + + self.update_dn(jid,newDn) + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Ahenk başarı ile taşındı.', + data=json.dumps({'Dn': newDn}), + content_type=ContentType.APPLICATION_JSON.value) + + + except Exception as e: + self.logger.error(" error on handle xmessage task. Error: " + str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Ahenk taşınırken hata olustu' + str(e), + content_type=ContentType.APPLICATION_JSON.value) + + +def handle_task(task, context): + cls = MoveAgent(task, context) + cls.handle_task() diff --git a/src/plugins/ldap/init.py b/src/plugins/ldap/init.py new file mode 100644 index 0000000..f8162b3 --- /dev/null +++ b/src/plugins/ldap/init.py @@ -0,0 +1,20 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Init(AbstractPlugin): + def __init__(self, context): + super(Init, self).__init__() + self.context = context + self.logger = self.get_logger() + + def handle_mode(self): + # TODO Do what do you want to do! + pass + + +def handle_mode(context): + init = Init(context) + init.handle_mode() diff --git a/src/plugins/ldap/login.py b/src/plugins/ldap/login.py new file mode 100644 index 0000000..5e8119c --- /dev/null +++ b/src/plugins/ldap/login.py @@ -0,0 +1,21 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Login(AbstractPlugin): + def __init__(self, context): + super(Login, self).__init__() + self.context = context + self.username = str(context.get_username()) + self.logger = self.get_logger() + + def handle_mode(self): + # TODO Do what do you want to do! + pass + + +def handle_mode(context): + login = Login(context) + login.handle_mode() diff --git a/src/plugins/ldap/logout.py b/src/plugins/ldap/logout.py new file mode 100644 index 0000000..6c17985 --- /dev/null +++ b/src/plugins/ldap/logout.py @@ -0,0 +1,21 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Logout(AbstractPlugin): + def __init__(self, context): + super(Logout, self).__init__() + self.context = context + self.username = str(context.get_username()) + self.logger = self.get_logger() + + def handle_mode(self): + # TODO Do what do you want to do! + pass + + +def handle_mode(context): + logout = Logout(context) + logout.handle_mode() diff --git a/src/plugins/ldap/main.py b/src/plugins/ldap/main.py new file mode 100644 index 0000000..9d8d99b --- /dev/null +++ b/src/plugins/ldap/main.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +def info(): + inf = dict() + inf['name'] = 'ldap' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'Ldap management' + inf['task'] = True + inf['user_oriented'] = True + inf['machine_oriented'] = True + inf['developer'] = 'muhammededip.yildiz@tubitak.gov.tr' + + return inf diff --git a/src/plugins/ldap/move_agent.py b/src/plugins/ldap/move_agent.py new file mode 100644 index 0000000..89085d1 --- /dev/null +++ b/src/plugins/ldap/move_agent.py @@ -0,0 +1,66 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Edip YILDIZ +# Author: Tuncay ÇOLAK + + +from base.model.enum.content_type import ContentType +import json, threading + + +from base.plugin.abstract_plugin import AbstractPlugin + +import threading + + +class MoveAgent(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + + + def update_dn(self, jid, newDn): + cols = ['dn']; + values = [newDn] + return self.db_service.update('registration', cols, values, 'jid=\''+jid+'\'') + + + + def getCnFromDn(self,dn): + if dn !=None and str(dn) !="": + dnStrArr = str(dn).split(",") + if len(dnStrArr)>0: + return dnStrArr[0] + + + def handle_task(self): + try: + dn = self.data['dn'] + newParentDn = self.data['newParentDn'] + + jid= self.db_service.select_one_result('registration','jid','registered = 1') + + newDn=str(dn).replace(dn, self.getCnFromDn(dn)+ str(newParentDn)) + + self.update_dn(jid,newDn) + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Ahenk başarı ile taşındı.', + data=json.dumps({'Dn': newDn}), + content_type=ContentType.APPLICATION_JSON.value) + + + except Exception as e: + self.logger.error(" error on handle xmessage task. Error: " + str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Ahenk taşınırken hata olustu' + str(e), + content_type=ContentType.APPLICATION_JSON.value) + + +def handle_task(task, context): + cls = MoveAgent(task, context) + cls.handle_task() diff --git a/src/plugins/ldap/policy.py b/src/plugins/ldap/policy.py new file mode 100644 index 0000000..fad11ac --- /dev/null +++ b/src/plugins/ldap/policy.py @@ -0,0 +1,155 @@ +# !/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Conky(AbstractPlugin): + def __init__(self, data, context): + super(Conky, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.machine_profile = True + self.conky_config_file_dir = '/etc/conky/' + self.conky_config_file_path = '/etc/conky/conky.conf' + self.command_autorun_conky = 'sleep 3;conky -d {0} -c {1}' + self.username = None + self.autostart_dir_path = '{0}.config/autostart/' + self.autorun_file_path = '{0}conky.desktop' + self.logger.debug('Parameters were initialized.') + + def handle_policy(self): + try: + + # Checking dependecies + if self.check_dependencies(['conky', 'conky-all']) is True: + self.logger.debug('Dependencies checked.') + else: + return + + # Killing conky processes + self.logger.debug('Conky named processes will be killed.') + self.execute('killall -9 conky') + + # Is user profile + if 'username' in self.context.data and self.context.get('username') is not None: + self.logger.debug('This is user profile, parameters reinitializing.') + self.username = self.context.get('username') + self.conky_config_file_dir = '{0}.conky/'.format(self.Sessions.user_home_path(self.username)) + self.conky_config_file_path = '{0}conky.conf'.format(self.conky_config_file_dir) + self.machine_profile = False + + # Creating/checking conky file dir and conky conf file + self.logger.debug('Conky file directory and configuration file is creating/checking') + if self.is_exist(self.conky_config_file_dir): + self.logger.debug('Old config file will be deleted.') + self.delete_file(self.conky_config_file_path) + else: + self.logger.debug( + 'Creating directory for conky config at {0}'.format(self.conky_config_file_dir)) + self.create_directory(self.conky_config_file_dir) + + if self.create_file(self.conky_config_file_path): + self.logger.debug('Config file was created.') + self.write_file(self.conky_config_file_path, json.loads(self.data)['message']) + self.logger.debug('Config file was filled by context.') + else: + self.logger.error('A problen occurred while creating Conky configuration file.') + raise Exception('File {0} could not created.'.format(self.conky_config_file_path)) + + # Creating autorun + self.logger.debug('Creating autorun file...') + self.initialize_auto_run() + + if self.machine_profile is False: + self.execute( + self.command_autorun_conky.format('--display=' + self.Sessions.display(self.username), + self.conky_config_file_path), + as_user=self.username, result=False) + + self.execute('chown -hR ' + self.username + ':' + self.username + ' ' + self.conky_config_file_dir) + self.logger.debug('Owner of Conky config file was changed.') + else: + self.execute(self.command_autorun_conky.format('', self.conky_config_file_path), result=False) + + self.logger.debug('Autorun command executed successfully') + self.context.create_response(code=self.get_message_code().POLICY_PROCESSED.value, + message='Conky politikası başarıyla çalıştırıldı.') + + except Exception as e: + self.logger.error( + 'A problem occurred while handling Conky policy. Error Message: {}'.format(str(e))) + self.context.create_response(code=self.get_message_code().POLICY_ERROR.value, + message='Conky politikası uygulanırken bir hata oluştu.') + + def check_dependencies(self, packages): + + self.logger.debug('Checking dependencies') + for package in packages: + if self.is_installed(package) is False: + self.logger.debug('Could not found {0}. It will be installed'.format(package)) + result_code, p_out, p_err = self.install_with_apt_get(package) + if result_code == 0: + self.logger.debug('{0} installed successfully'.format(package)) + else: + self.logger.error( + 'A problem occurred while installing {0} package. Error Message: {1}'.format(package, + str( + p_err))) + self.context.create_response(code=self.get_message_code().POLICY_ERROR.value, + message='Bağımlılıklardan {0} paketi kurulurken hata oluştu.') + return False + + return True + + def initialize_auto_run(self): + + if self.machine_profile is True: + self.logger.debug('All users conky configuration files will be removed because of machine profile') + if self.Sessions.user_name() is not None and len(self.Sessions.user_name()) > 0: + for username in self.Sessions.user_name(): + self.logger.debug( + 'Removing conf file of user {0}'.format(username)) + self.delete_file( + self.autorun_file_path.format( + self.autostart_dir_path.format(self.Sessions.user_home_path(username)))) + else: + self.logger.debug( + 'There are no user') + + else: + home_path = self.Sessions.user_home_path(self.username) + self.logger.debug( + 'Creating autorun file for user {0}'.format(self.username)) + self.create_autorun_file(self.autostart_dir_path.format(home_path), + self.conky_config_file_path, + self.autorun_file_path.format(self.autostart_dir_path.format(home_path))) + self.logger.debug( + 'Autorun created') + + def create_autorun_file(self, autostart_path, conky_config_file_path, autorun_file_path): + if not self.is_exist(autostart_path): + self.logger.debug( + 'Creating file: {0}'.format(autostart_path)) + self.create_directory(autostart_path) + + file_content = '[Desktop Entry]\n' \ + 'Encoding=UTF-8 \n' \ + 'Type=Application \n' \ + 'Name=Conky \n' \ + 'Comment=Conky Monitor \n' \ + 'Exec=conky -d -c ' + conky_config_file_path + '\n' \ + 'StartupNotify=false \n' \ + 'Terminal=false \n' + self.logger.debug( + 'Writing content to autorun file.') + self.write_file(autorun_file_path, file_content, 'w') + + +def handle_policy(profile_data, context): + plugin = Conky(profile_data, context) + plugin.handle_policy() diff --git a/src/plugins/ldap/rename_entry.py b/src/plugins/ldap/rename_entry.py new file mode 100644 index 0000000..4d7c71b --- /dev/null +++ b/src/plugins/ldap/rename_entry.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Edip YILDIZ +# Author: Tuncay ÇOLAK + + +from base.model.enum.content_type import ContentType +import json, threading + + +from base.plugin.abstract_plugin import AbstractPlugin + +import threading + + +class UpdateEntry(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + + + def update_dn(self, jid, newDn): + cols = ['dn']; + values = [newDn] + return self.db_service.update('registration', cols, values, 'jid=\''+jid+'\'') + + + def handle_task(self): + try: + dn = self.data['dn'] + jid= self.db_service.select_one_result('registration','jid','registered = 1') + + cn = self.data['oldCn'] + newCn = self.data['newCn'] + + newDn=str(dn).replace(cn,newCn) + + self.update_dn(jid,newDn) + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Ahenk adı başarı ile değiştirildi.', + data=json.dumps({'Dn': newDn}), + content_type=ContentType.APPLICATION_JSON.value) + + + except Exception as e: + self.logger.error(" error on handle xmessage task. Error: " + str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Ahenk adı değiştirilirken hata olustu' + str(e), + content_type=ContentType.APPLICATION_JSON.value) + + +def handle_task(task, context): + cls = UpdateEntry(task, context) + cls.handle_task() diff --git a/src/plugins/ldap/safe.py b/src/plugins/ldap/safe.py new file mode 100644 index 0000000..2debd2e --- /dev/null +++ b/src/plugins/ldap/safe.py @@ -0,0 +1,21 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Safe(AbstractPlugin): + def __init__(self, context): + super(Safe, self).__init__() + self.context = context + self.username = str(context.get_username()) + self.logger = self.get_logger() + + def handle_safe_mode(self): + # TODO Do what do you want to do! + pass + + +def handle_mode(context): + safe = Safe(context) + safe.handle_safe_mode() diff --git a/src/plugins/ldap/shutdown.py b/src/plugins/ldap/shutdown.py new file mode 100644 index 0000000..cda1972 --- /dev/null +++ b/src/plugins/ldap/shutdown.py @@ -0,0 +1,20 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Shutdown(AbstractPlugin): + def __init__(self, context): + super(Shutdown, self).__init__() + self.context = context + self.logger = self.get_logger() + + def handle_mode(self): + # TODO Do what do you want to do! + pass + + +def handle_mode(context): + shutdown = Shutdown(context) + shutdown.handle_mode() diff --git a/src/plugins/ldap/task_command_id.py b/src/plugins/ldap/task_command_id.py new file mode 100644 index 0000000..50651f7 --- /dev/null +++ b/src/plugins/ldap/task_command_id.py @@ -0,0 +1,23 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Sample(AbstractPlugin): + def __init__(self, task, context): + super(Sample, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + + def handle_task(self): + # TODO Do what do you want to do! + # TODO Don't Forget returning response with + pass + + +def handle_task(task, context): + print('Sample Plugin Task') + sample = Sample(task, context) + sample.handle_task() diff --git a/src/plugins/local-user/add_user.py b/src/plugins/local-user/add_user.py new file mode 100644 index 0000000..252bcb4 --- /dev/null +++ b/src/plugins/local-user/add_user.py @@ -0,0 +1,149 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN +# Author:Tuncay ÇOLAK + +from base.plugin.abstract_plugin import AbstractPlugin +from pathlib import Path + +class AddUser(AbstractPlugin): + def __init__(self, task, context): + super(AddUser, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.username = self.task['username'] + self.password = self.task['password'] + self.home = self.task['home'] + self.active = self.task['active'] + self.groups = self.task['groups'] + self.desktop_write_permission = self.task['desktop_write_permission'] + self.kiosk_mode = self.task['kiosk_mode'] + + self.script = '/bin/bash ' + self.Ahenk.plugins_path() + 'local-user/scripts/{0}' + + self.add_user = 'useradd -d {0} {1}' + self.check_home_owner = 'stat -c \'%U\' {}' + self.enable_user = 'passwd -u {}' + self.disable_user = 'passwd -l {}' + self.add_user_to_groups = 'usermod -a -G {0} {1}' + self.create_shadow_password = 'mkpasswd -m sha-512 {}' + self.change_password = 'usermod -p {0} {1}' + self.change_shell = 'usermod -s /bin/bash {}' + self.change_owner = 'chown {0}.{0} {1}' + self.change_permission = 'chmod 755 {}' + + self.desktop_path = '' + self.xfce4_session = "/usr/bin/xfce4-session" + self.gnome_session = "/usr/bin/gnome-session" + self.desktop_env = None + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + self.desktop_env = self.get_desktop_env() + self.logger.info("Get desktop environment is {0}".format(self.desktop_env)) + + if not self.is_exist(self.home): + self.create_directory(self.home) + self.execute(self.add_user.format(self.home, self.username)) + self.logger.debug('Added new user: {0}, home: {1}'.format(self.username, self.home)) + + self.execute(self.change_owner.format(self.username, self.home)) + self.execute(self.change_permission.format(self.home)) + self.logger.debug('Changed owner and permission for home directory.') + + if self.groups != "": + self.execute(self.add_user_to_groups.format(self.groups, self.username)) + self.logger.debug('Added user to these groups: {}'.format(self.groups)) + + if str(self.password).strip() != "": + result_code, p_out, p_err = self.execute(self.create_shadow_password.format(self.password)) + shadow_password = p_out.strip() + # shadow_password = crypt.crypt(self.password) + self.execute(self.change_password.format('\'{}\''.format(shadow_password), self.username)) + self.logger.debug('Changed password.') + + self.execute(self.change_shell.format(self.username)) + self.logger.debug('Changed user shell to /bin/bash') + + if self.active == "true": + self.execute(self.enable_user.format(self.username)) + self.logger.debug('The user has been enabled.') + elif self.active == "false": + self.execute(self.disable_user.format(self.username)) + self.logger.debug('The user has been disabled.') + + agent_language = self.get_language() + if agent_language == "tr_TR": + desktop_name = "Masaüstü" + else: + desktop_name = "Desktop" + + self.execute("mkdir " + self.home + "/" + desktop_name) + self.desktop_path = self.home + "/" + desktop_name + self.execute(self.change_owner.format(self.username, self.desktop_path)) + self.logger.debug('owner is changed for user {0} directory'.format(desktop_name)) + + if self.desktop_write_permission == "true": + self.set_permission(self.desktop_path, 775) + self.logger.debug('Desktop write permission is true') + + elif self.desktop_write_permission == "false": + self.set_permission(self.desktop_path, 575) + self.logger.debug('Desktop write permission is false') + # + # Handle kiosk mode + # + if self.desktop_env == "xfce": + result_code, p_out, p_err = self.execute(self.script.format('find_locked_users.sh'), result=True) + if result_code != 0: + self.logger.error( + 'Error occurred while managing kiosk mode.') + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Masaüstü kilidi ayarlanırken hata oluştu.') + return + locked_users = [] + if p_out: + self.logger.debug('pout {0}'.format(str(p_out))) + locked_users = p_out.strip().split(';') + + if self.kiosk_mode == "true": + self.logger.debug('Kiosk mode is active {0}'.format(str(locked_users))) + if self.username not in locked_users: + self.logger.debug('Adding user {0} to locked users'.format(self.username)) + locked_users.append(self.username) + locked_users_str = ";".join(locked_users) + self.logger.debug('Users: {0}'.format(locked_users_str)) + comm = "sed -i 's/^.*" + '' + "/' /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml" + result_code1, p_out1, p_err1 = self.execute(comm) + elif self.kiosk_mode == "false": + self.logger.debug('Kiok mode is NOT active') + if self.username in locked_users: + self.logger.debug('Removing user {0} from locked users'.format(self.username)) + locked_users.remove(self.username) + if locked_users: + locked_users_str = ";".join(locked_users) + # if xfce4-panel.xml doesn not exist copy it from ~/.config/xfce4/xfconf/xfce-perchannel-xml/ + comm = "sed -i 's/^.*" + '' + "/' /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml" + result_code1, p_out1, p_err1 = self.execute(comm) + else: + self.execute(self.script.format('remove_locked_users.sh ')) + else: + self.logger.info("Desktop environ is GNOME. Kiosk mode not setting") + self.logger.info('User has been added successfully.') + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Kullanıcı başarıyla eklendi.') + + except Exception as e: + self.logger.error('A problem occurred while handling Local-User task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Local-User görevi çalıştırılırken bir hata oluştu.') + + +def handle_task(task, context): + add_user = AddUser(task, context) + add_user.handle_task() diff --git a/src/plugins/local-user/delete_user.py b/src/plugins/local-user/delete_user.py new file mode 100644 index 0000000..cf021ed --- /dev/null +++ b/src/plugins/local-user/delete_user.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + +class DeleteUser(AbstractPlugin): + def __init__(self, task, context): + super(DeleteUser, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.username = self.task['username'] + self.home = self.task['home'] + self.delete_home = self.task['delete_home'] + + self.delete_user_home = 'rm -r {}' + self.delete_user = 'userdel {}' + self.logout_user = 'pkill -u {}' + self.kill_all_process = 'killall -KILL -u {}' + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + self.execute(self.logout_user.format(self.username)) + self.execute(self.kill_all_process.format(self.username)) + self.logger.debug('Killed all processes for {}'.format(self.username)) + + if self.delete_home is True: + self.execute(self.delete_user.format(self.username)) + self.execute(self.delete_user_home.format(self.home)) + self.logger.debug('Deleted user with home: {}'.format(self.username)) + elif self.delete_home is False: + self.execute(self.delete_user.format(self.username)) + self.logger.debug('Deleted user: {}'.format(self.username)) + + self.logger.info('User has been deleted successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Kullanıcı başarıyla silindi.') + + except Exception as e: + self.logger.error( + 'A problem occured while handling Local-User task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Local-User görevi çalıştırılırken bir hata oluştu.') + + +def handle_task(task, context): + delete_user = DeleteUser(task, context) + delete_user.handle_task() diff --git a/src/plugins/local-user/edit_user.py b/src/plugins/local-user/edit_user.py new file mode 100644 index 0000000..f55f799 --- /dev/null +++ b/src/plugins/local-user/edit_user.py @@ -0,0 +1,160 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN +# Author:Tuncay ÇOLAK + +from base.plugin.abstract_plugin import AbstractPlugin +from pathlib import Path + +class EditUser(AbstractPlugin): + def __init__(self, task, context): + super(EditUser, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.username = self.task['username'] + self.new_username = self.task['new_username'] + self.password = self.task['password'] + self.home = self.task['home'] + self.active = self.task['active'] + self.groups = self.task['groups'] + self.desktop_write_permission = self.task['desktop_write_permission'] + self.kiosk_mode = self.task['kiosk_mode'] + self.current_home = self.execute('eval echo ~{0}'.format(self.username))[1] + + self.script = '/bin/bash ' + self.Ahenk.plugins_path() + 'local-user/scripts/{0}' + + self.kill_processes = 'pkill -u {}' + self.change_username = 'usermod -l {0} {1}' + self.create_shadow_password = 'mkpasswd -m sha-512 {}' + self.change_password = 'usermod -p {0} {1}' + self.change_home = 'usermod -m -d {0} {1}' + self.enable_user = 'passwd -u {}' + self.disable_user = 'passwd -l {}' + self.change_groups = 'usermod -G {0} {1}' + self.remove_all_groups = 'usermod -G "" {}' + self.change_owner = 'chown {0}.{0} {1}' + self.change_permission = 'chmod 755 {}' + self.logout_user = 'pkill -u {}' + self.kill_all_process = 'killall -KILL -u {}' + + self.message = '' + self.message_code_level = 1 + + self.xfce4_session = "/usr/bin/xfce4-session" + self.gnome_session = "/usr/bin/gnome-session" + self.desktop_env = None + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + self.desktop_env = self.get_desktop_env() + self.logger.info("Get desktop environment is {0}".format(self.desktop_env)) + + self.execute(self.logout_user.format(self.username)) + self.execute(self.kill_all_process.format(self.username)) + self.logger.debug('Killed all processes for {}'.format(self.username)) + + if str(self.new_username).strip() != "": + self.execute(self.kill_processes.format(self.username)) + self.execute(self.change_username.format(self.new_username, self.username)) + self.logger.debug('Changed username {0} to {1}'.format(self.username, self.new_username)) + self.username = self.new_username + + if str(self.password).strip() != "": + result_code, p_out, p_err = self.execute(self.create_shadow_password.format(self.password)) + shadow_password = p_out.strip() + self.execute(self.change_password.format('\'{}\''.format(shadow_password), self.username)) + self.logger.debug('Changed password.') + + if self.current_home != self.home: + self.execute(self.kill_processes.format(self.username)) + self.execute(self.change_home.format(self.home, self.username)) + self.logger.debug('Changed home directory to: {}'.format(self.home)) + + self.execute(self.change_owner.format(self.username, self.home)) + self.execute(self.change_permission.format(self.home)) + self.logger.debug('Changed owner and permission for home directory.') + + if self.active == "true": + self.execute(self.enable_user.format(self.username)) + self.logger.debug('The user has been enabled.') + elif self.active == "false": + self.execute(self.disable_user.format(self.username)) + self.logger.debug('The user has been disabled.') + + if self.groups != "": + self.execute(self.change_groups.format(self.groups, self.username)) + self.logger.debug('Added user to these groups: {}'.format(self.groups)) + else: + self.execute(self.remove_all_groups.format(self.username)) + self.logger.debug('Removed all groups for user: {}'.format(self.username)) + + agent_language = self.get_language() + if agent_language == "tr_TR": + desktop_name = "Masaüstü" + else: + desktop_name = "Desktop" + if self.desktop_write_permission == "true": + self.set_permission(self.current_home.strip() + "/" + desktop_name, 775) + self.logger.debug('Desktop write permission is true') + + elif self.desktop_write_permission == "false": + self.set_permission(self.current_home.strip() + "/" + desktop_name, 575) + self.logger.debug('Desktop write permission is false') + # + # Handle kiosk mode + # + if self.desktop_env == "xfce": + result_code, p_out, p_err = self.execute(self.script.format('find_locked_users.sh'), result=True) + if result_code != 0: + self.logger.error('Error occurred while managing kiosk mode.') + self.message_code_level += 1 + self.message = 'Masaüstü kilidi ayarlanırken hata oluştu.' + locked_users = [] + if p_out: + self.logger.debug('pout {0}'.format(str(p_out))) + locked_users = p_out.strip().split(';') + + if self.kiosk_mode == "true": + self.logger.debug('Kiosk mode is active {0}'.format(str(locked_users))) + if self.username not in locked_users: + self.logger.debug('Adding user {0} to locked users'.format(self.username)) + locked_users.append(self.username) + locked_users_str = ";".join(locked_users) + self.logger.debug('Users: {0}'.format(locked_users_str)) + comm = "sed -i 's/^.*" + '' + "/' /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml" + result_code1, p_out1, p_err1 = self.execute(comm) + elif self.kiosk_mode == "false": + self.logger.debug('Kiok mode is NOT active') + if self.username in locked_users: + self.logger.debug('Removing user {0} from locked users'.format(self.username)) + locked_users.remove(self.username) + if locked_users: + locked_users_str = ";".join(locked_users) + comm = "sed -i 's/^.*" + '' + "/' /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml" + result_code1, p_out1, p_err1 = self.execute(comm) + else: + self.execute(self.script.format('remove_locked_users.sh ')) + else: + self.logger.info("Desktop environ is GNOME. Kiosk mode not setting") + self.logger.info('User has been edited successfully.') + + if self.message_code_level == 1: + response_code = self.message_code.TASK_PROCESSED.value + response_message = 'Kullanıcı başarıyla düzenlendi.' + else: + response_code = self.message_code.TASK_WARNING.value + response_message = 'Kullanıcı düzenlendi; fakat {0}'.format(self.message) + self.context.create_response(code=response_code, message=response_message) + + except Exception as e: + self.logger.error('A problem occurred while handling Local-User task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Local-User görevi çalıştırılırken bir hata oluştu.') + +def handle_task(task, context): + edit_user = EditUser(task, context) + edit_user.handle_task() diff --git a/src/plugins/local-user/get_groups.py b/src/plugins/local-user/get_groups.py new file mode 100644 index 0000000..4b1086d --- /dev/null +++ b/src/plugins/local-user/get_groups.py @@ -0,0 +1,42 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import json +from base.plugin.abstract_plugin import AbstractPlugin + +class GetGroups(AbstractPlugin): + def __init__(self, task, context): + super(GetGroups, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.command_get_groups = 'cut -d: -f1 /etc/group' + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + + try: + result_code, p_out, p_err = self.execute(self.command_get_groups) + groups = p_out.split('\n') + groups.pop() + + self.logger.debug('groups: {0}'.format(groups)) + + self.logger.info('Local User \'get_groups\' task is handled successfully') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Grup listesi başarıyla getirildi.', + data=json.dumps({'groups': groups}), + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.error('A problem occurred while handling Local-User \'get_groups\' task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Local-User \'get_groups\' görevi çalıştırılırken bir hata oluştu.') + +def handle_task(task, context): + get_groups = GetGroups(task, context) + get_groups.handle_task() diff --git a/src/plugins/local-user/get_users.py b/src/plugins/local-user/get_users.py new file mode 100644 index 0000000..bdd5964 --- /dev/null +++ b/src/plugins/local-user/get_users.py @@ -0,0 +1,130 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN +# Author:Tuncay ÇOLAK + +import json +from pathlib import Path + +from base.plugin.abstract_plugin import AbstractPlugin + +class GetUsers(AbstractPlugin): + def __init__(self, task, context): + super(GetUsers, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + self.script = '/bin/bash ' + self.Ahenk.plugins_path() + 'local-user/scripts/{0}' + + self.command_users = 'awk -F: \'{print $1 ":" $6 ":" $7}\' /etc/passwd | grep /bin/bash' + self.command_user_groups = 'groups {}' + self.command_not_active = 'egrep \':\!\' /etc/shadow |awk -F: \'{print $1}\'' + self.command_get_groups = 'cut -d: -f1 /etc/group' + self.xfce4_session = "/usr/bin/xfce4-session" + self.gnome_session = "/usr/bin/gnome-session" + self.desktop_env = None + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + + try: + user_list = [] + result_code, p_out, p_err = self.execute(self.command_users) + lines = p_out.split('\n') + lines.pop() + + self.desktop_env = self.get_desktop_env() + self.logger.info("Get desktop environment is {0}".format(self.desktop_env)) + + for line in lines: + detail = line.split(':') + + result_code, p_out, p_err = self.execute(self.command_user_groups.format(str(detail[0]).strip())) + groups = p_out.split(':') + groups[1] = str(groups[1]).strip() + groups[1] = groups[1].replace("'", "").replace(" ", ", ") + is_active = 'true' + result_code, p_out, p_err = self.execute(self.command_not_active) + users = p_out.split('\n') + + if str(detail[0]).strip() in users: + is_active = 'false' + + self.desktop_path = '' + if self.is_exist("{0}/Masaüstü/".format(str(detail[1]).strip())): + self.desktop_path = "{0}/Masaüstü/".format(str(detail[1]).strip()) + self.logger.debug("Desktop path for user '{0}' : {1}".format(str(detail[0]).strip(), self.desktop_path)) + elif self.is_exist("{0}/Desktop/".format(str(detail[1]).strip())): + self.desktop_path = "{0}/Desktop/".format(str(detail[1]).strip()) + self.logger.debug("Desktop path for user '{0}' : {1}".format(str(detail[0]).strip(), self.desktop_path)) + else: + self.logger.debug( + 'Desktop write permission could not get. Desktop path not found for user "{0}"'.format( + str(detail[0]).strip())) + + result_code, p_out, p_err = self.execute(' stat -c "%a %n" ' + self.desktop_path) + self.logger.debug('sudo stat -c "%a %n" ' + self.desktop_path) + is_desktop_write_permission_exists = 'false' + if result_code == 0: + permission_codes = p_out.split() + self.logger.debug("permission codes : " + str(permission_codes)) + if len(permission_codes) > 0: + permission_code = permission_codes[0].strip() + self.logger.debug("permission code is : " + permission_code) + if permission_code == "775": + is_desktop_write_permission_exists = 'true' + + if self.desktop_env == "xfce": + is_kiosk_mode_on = 'false' + self.logger.debug('Kiosk mode info will be taken') + file_xfce4_panel = Path("/etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml") + if not file_xfce4_panel.exists(): + self.logger.error( + 'PANEL XML NOT FOUND COPY') + source_path = "{0}local-user/panelconf/xfce4-panel.xml".format(self.Ahenk.plugins_path()) + self.logger.info("----->>>>" + source_path) + self.copy_file(source_path, "/etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml") + self.logger.error( + 'FILE IS COPIED') + result_code, p_out, p_err = self.execute(self.script.format('find_locked_users.sh'), result=True) + if result_code != 0: + self.logger.error( + 'Error occurred while finding locked users.') + if p_out: + self.logger.debug('locked users are {0}'.format(str(p_out))) + locked_users = p_out.strip().split(';') + # self.logger.debug("user is " + str(detail[0]).strip()) + # self.logger.debug("locked users are " + str(locked_users)) + if str(detail[0]).strip() in locked_users: + is_kiosk_mode_on = 'true' + self.logger.debug('Desktop environ is XFCE. Kiosk mode info is taken') + else: + is_kiosk_mode_on = "true" + self.logger.info("Desktop environ is GNOME. Return kiok mode TRUE") + + user = {'user': str(detail[0]).strip(), 'groups': groups[1], 'home': detail[1], 'is_active': is_active, 'is_desktop_write_permission_exists': is_desktop_write_permission_exists, 'is_kiosk_mode_on': is_kiosk_mode_on} + user_list.append(user) + self.logger.debug('user: {0}, groups: {1}, home: {2}, is_active: {3}'.format(str(detail[0]).strip(), groups[1], detail[1], is_active)) + self.logger.info('Local User task is handled successfully') + # + # get all groups + # + result_code, p_out, p_err = self.execute(self.command_get_groups) + all_groups = p_out.split('\n') + all_groups.pop() + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Kullanıcı listesi başarıyla getirildi.', + data=json.dumps({'users': user_list, 'all_groups': all_groups}), + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.error('A problem occurred while handling Local-User task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Local-User görevi çalıştırılırken bir hata oluştu.') + + +def handle_task(task, context): + get_users = GetUsers(task, context) + get_users.handle_task() diff --git a/src/plugins/local-user/init.py b/src/plugins/local-user/init.py new file mode 100644 index 0000000..f69217d --- /dev/null +++ b/src/plugins/local-user/init.py @@ -0,0 +1,24 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Init(AbstractPlugin): + def __init__(self, context): + super(Init, self).__init__() + self.context = context + self.logger = self.get_logger() + + self.logger.debug('Parameters were initialized.') + + def handle_mode(self): + if self.is_installed('whois') is False: + self.install_with_apt_get('whois') + self.logger.debug('whois has been installed with apt-get.') + + +def handle_mode(context): + init = Init(context) + init.handle_mode() diff --git a/src/plugins/local-user/main.py b/src/plugins/local-user/main.py new file mode 100644 index 0000000..bf7d87e --- /dev/null +++ b/src/plugins/local-user/main.py @@ -0,0 +1,15 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +def info(): + inf = dict() + inf['name'] = 'local-user' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'Local-User plugin provides to listing users and adding, editing, deleting a local user.' + inf['task'] = True + inf['user_oriented'] = False + inf['machine_oriented'] = False + inf['developer'] = 'tuncay.colak@tubitak.gov.tr' + + return inf diff --git a/src/plugins/local-user/panelconf/xfce4-panel.xml b/src/plugins/local-user/panelconf/xfce4-panel.xml new file mode 100644 index 0000000..cabe1ab --- /dev/null +++ b/src/plugins/local-user/panelconf/xfce4-panel.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/local-user/scripts/find_locked_users.sh b/src/plugins/local-user/scripts/find_locked_users.sh new file mode 100644 index 0000000..5ce4728 --- /dev/null +++ b/src/plugins/local-user/scripts/find_locked_users.sh @@ -0,0 +1,3 @@ +#!/bin/bash +sed -n 's/^.*locked="\([A-Za-z0-9; ]*\)".*$/\1/p' /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml +#sed -n 's/^.*locked="\([A-Za-z0-9; ]*\)".*$/\1/p' ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml diff --git a/src/plugins/local-user/scripts/remove_locked_users.sh b/src/plugins/local-user/scripts/remove_locked_users.sh new file mode 100644 index 0000000..19f5f46 --- /dev/null +++ b/src/plugins/local-user/scripts/remove_locked_users.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +sed -i 's/\(^.*\)\(locked="[A-Za-z; ]*"\)\(.*$\)/\1\3/' /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml +#sed -i 's/\(^.*\)\(locked="[A-Za-z; ]*"\)\(.*$\)/\1\3/' ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml diff --git a/src/plugins/login-manager/init.py b/src/plugins/login-manager/init.py new file mode 100644 index 0000000..80313ad --- /dev/null +++ b/src/plugins/login-manager/init.py @@ -0,0 +1,23 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Init(AbstractPlugin): + def __init__(self, context): + super(Init, self).__init__() + self.context = context + self.logger = self.get_logger() + + self.logger.debug('Parameters were initialized.') + + def handle_init_mode(self): + self.logger.debug('Removing login-manager cron job if exist...') + self.execute('crontab -l | sed \'/{0}/d\' | crontab -'.format('login-manager/scripts/check.py')) + + +def handle_mode(context): + init = Init(context) + #init.handle_init_mode() diff --git a/src/plugins/login-manager/machine_shutdown.py b/src/plugins/login-manager/machine_shutdown.py new file mode 100644 index 0000000..d8af647 --- /dev/null +++ b/src/plugins/login-manager/machine_shutdown.py @@ -0,0 +1,33 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Tuncay ÇOLAK +## shutdown agents + +from base.plugin.abstract_plugin import AbstractPlugin + +class LoginManager(AbstractPlugin): + def __init__(self, task, context): + super(AbstractPlugin, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.command_shutdown = 'shutdown -h now' + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + result_code, p_out, p_err = self.execute(self.command_shutdown) + self.logger.info("shutdown agent success") + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='İstemci başarıyla kapatıldı.') + except Exception as e: + self.logger.error('A problem occured while handling Login-Manager task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='İstemci kapatılırken bir hata oluştu.') + +def handle_task(task, context): + manage = LoginManager(task, context) + manage.handle_task() diff --git a/src/plugins/login-manager/main.py b/src/plugins/login-manager/main.py new file mode 100644 index 0000000..359cf1b --- /dev/null +++ b/src/plugins/login-manager/main.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +def info(): + inf = dict() + inf['name'] = 'login-manager' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'Login-Manager plugin provides to managing sessions for users or machine.' + inf['task'] = True + inf['user_oriented'] = True + inf['machine_oriented'] = True + inf['developer'] = 'mine.dogan@agem.com.tr' + + return inf diff --git a/src/plugins/login-manager/manage.py b/src/plugins/login-manager/manage.py new file mode 100644 index 0000000..af67af9 --- /dev/null +++ b/src/plugins/login-manager/manage.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import datetime + +from base.plugin.abstract_plugin import AbstractPlugin + + +class LoginManager(AbstractPlugin): + def __init__(self, task, context): + super(AbstractPlugin, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.command_logout_user = 'killall --user {0}' + self.command_get_users_currently_login = "who | cut -d' ' -f1 | sort | uniq" + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + + result_code, p_out, p_err = self.execute(self.command_get_users_currently_login) + users = [] + + if p_out != None: + users = str(p_out).split('\n') + users.pop() + + for user in users: + self.logger.debug('End session for user: {0}'.format(user)) + self.execute(self.command_logout_user.format(user)) + + self.logger.info('Login-Manager task is handled successfully') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Oturumlar başarıyla sonlandırıldı.') + + except Exception as e: + self.logger.error('A problem occured while handling Login-Manager task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Login-Manager görevi çalıştırılırken bir hata oluştu.') + + +def handle_task(task, context): + manage = LoginManager(task, context) + manage.handle_task() diff --git a/src/plugins/login-manager/policy.py b/src/plugins/login-manager/policy.py new file mode 100644 index 0000000..f0a5b91 --- /dev/null +++ b/src/plugins/login-manager/policy.py @@ -0,0 +1,82 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import configparser +import datetime +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class LoginManager(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.username = self.context.get('username') + + self.parameters = json.loads(self.data) + + self.days = self.parameters['days'] + self.start_time = self.parameters['start-time'] + self.end_time = self.parameters['end-time'] + self.last_date = datetime.datetime.strptime(str(self.parameters['last-date']), "%d/%m/%Y").date() + self.duration = self.parameters['duration'] + + self.command_cron_control = 'crontab -l | grep login-manager/scripts/{}' + + self.logger.debug('Parameters were initialized.') + + def handle_policy(self): + try: + config = configparser.RawConfigParser() + config.add_section('PERMISSION') + + config.set('PERMISSION', 'days', str(self.days)) + config.set('PERMISSION', 'start_time', str(self.start_time)) + config.set('PERMISSION', 'end_time', str(self.end_time)) + config.set('PERMISSION', 'last_date', str(self.last_date)) + config.set('PERMISSION', 'duration', str(self.duration)) + + if not self.is_exist('{0}login-manager/login_files'.format(self.Ahenk.plugins_path())): + self.create_directory('{0}login-manager/login_files'.format(self.Ahenk.plugins_path())) + + with open('{0}login-manager/login_files/{1}.permissions'.format(self.Ahenk.plugins_path(), self.username), + 'w') as configfile: + self.logger.debug('Writing parameters to configuration file...') + config.write(configfile) + + self.logger.debug('Making scripts executable...') + self.make_executable('{0}login-manager/scripts/cron.sh'.format(self.Ahenk.plugins_path())) + self.make_executable('{0}login-manager/scripts/check.py'.format(self.Ahenk.plugins_path())) + + self.logger.debug('Initial control for login permissions...') + self.execute('/usr/bin/python3 {0}login-manager/scripts/check.py {0}'.format(self.Ahenk.plugins_path())) + + result_code, p_out, p_err = self.execute(self.command_cron_control.format('check.py')) + + if p_out == '': + self.logger.debug('Creating a cron job to check session every minute...') + self.execute_script('{0}login-manager/scripts/cron.sh'.format(self.Ahenk.plugins_path()), [ + '* * * * * /usr/bin/python3 {0}login-manager/scripts/check.py {0}'.format( + self.Ahenk.plugins_path())]) + + self.logger.info('Session check has been started.') + self.context.create_response(code=self.message_code.POLICY_PROCESSED.value, + message='Oturum kontrolü başlatıldı.') + + + except Exception as e: + self.logger.error( + 'A problem occured while handling Login-Manager policy: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.POLICY_ERROR.value, + message='Login-Manager profili uygulanırken bir hata oluştu.') + + +def handle_policy(profile_data, context): + manage = LoginManager(profile_data, context) + manage.handle_policy() diff --git a/src/plugins/login-manager/safe.py b/src/plugins/login-manager/safe.py new file mode 100644 index 0000000..0e63281 --- /dev/null +++ b/src/plugins/login-manager/safe.py @@ -0,0 +1,33 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Safe(AbstractPlugin): + def __init__(self, context): + super(Safe, self).__init__() + self.context = context + self.username = str(context.get_username()) + self.logger = self.get_logger() + + self.logger.debug('Parameters were initialized.') + + def handle_safe_mode(self): + + user_permission_file = '{0}login-manager/login_files/{1}.permissions'.format(self.Ahenk.plugins_path(), + self.username) + if self.is_exist(user_permission_file): + self.logger.debug('Delete permission file for user \'{0}\'...'.format(self.username)) + self.delete_file(user_permission_file) + + machine_permission_file = '{0}login-manager/login_files/None.permissions'.format(self.Ahenk.plugins_path()) + if self.is_exist(machine_permission_file): + self.logger.debug('Delete permission file for machine...') + self.delete_file(machine_permission_file) + + +def handle_mode(context): + safe = Safe(context) + safe.handle_safe_mode() diff --git a/src/plugins/login-manager/scripts/check.py b/src/plugins/login-manager/scripts/check.py new file mode 100755 index 0000000..38432b3 --- /dev/null +++ b/src/plugins/login-manager/scripts/check.py @@ -0,0 +1,170 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import configparser +import datetime +import glob +import logging +import os +import subprocess, time +import sys + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))) + +from base.util.util import Util + + +class CheckTime: + def __init__(self): + super(self.__class__, self).__init__() + + if not os.path.exists('{0}login-manager/logs'.format(sys.argv[1])): + os.makedirs('{0}login-manager/logs'.format(sys.argv[1])) + + logging.basicConfig(filename='{0}login-manager/logs/check.log'.format(sys.argv[1]), filemode='w', + level=logging.DEBUG) + + self.files = glob.glob('{0}login-manager/login_files/*.permissions'.format(sys.argv[1])) + + self.username = 'None' + + self.days = '' + self.start_time = '' + self.end_time = '' + self.last_date = '' + self.duration = '' + + self.arr_start_time = '' + self.arr_end_time = '' + + self.today = datetime.datetime.today().weekday() + self.current_time = datetime.datetime.today().time() + self.current_date = datetime.datetime.today().date() + + self.start_minute = '' + self.end_minute = '' + self.current_minute = '' + + self.command_logout_user = 'killall --user {0}' + self.command_get_users_currently_login = "who | cut -d' ' -f1 | sort | uniq" + + logging.debug('Parameters were initialized.') + + def handle(self): + try: + + for file in self.files: + permission_file = str(file).replace('{0}login-manager/login_files/'.format(sys.argv[1]), '') + self.username = permission_file.replace('.permissions', '') + + config_parser = configparser.ConfigParser() + config_parser.read(file) + + logging.debug('Getting parameters from permission file for user \'{0}\''.format(self.username)) + + self.days = config_parser.get('PERMISSION', 'days') + self.start_time = config_parser.get('PERMISSION', 'start_time') + self.end_time = config_parser.get('PERMISSION', 'end_time') + self.last_date = datetime.datetime.strptime(str(config_parser.get('PERMISSION', 'last_date')), + "%Y-%m-%d").date() + self.duration = config_parser.get('PERMISSION', 'duration') + + logging.debug( + 'Days: {0}, Start Time: {1}, End Time: {2}, Last Date: {3}, Duration between notify and logout: {4}'.format( + self.days, self.start_time, self.end_time, self.last_date, self.duration)) + + self.arr_start_time = str(self.start_time).split(':') + self.arr_end_time = str(self.end_time).split(':') + + self.start_minute = int(self.arr_start_time[0]) * 60 + int(self.arr_start_time[1]) + self.end_minute = int(self.arr_end_time[0]) * 60 + int(self.arr_end_time[1]) + self.current_minute = int(self.current_time.hour) * 60 + int(self.current_time.minute) + + if self.username != 'None': + logging.debug('Writing to user profile...') + self.write_to_user_profile() + else: + logging.debug('Writing to global profile...') + self.write_to_global_profile() + + except Exception as e: + logging.error(e) + + def write_to_user_profile(self): + if str(self.today) in self.days: + + if not (self.start_minute < self.current_minute < self.end_minute and self.current_date <= self.last_date): + logging.debug('User \'{0}\' will log out.'.format(self.username)) + process = subprocess.Popen(self.command_logout_user.format(self.username), stdin=None, env=None, + cwd=None, stderr=subprocess.PIPE, + stdout=subprocess.PIPE, shell=True) + process.wait() + + elif int(self.end_minute) - int(self.current_minute) == int(self.duration): + Util.send_notify('Lider Ahenk', + 'Oturum {0} dakika sonra kapatılacak!'.format(str(self.duration)), + ':0', + self.username) + else: + Util.send_notify('Lider Ahenk', + 'Oturum şimdi kapatılacak!', + ':0', + self.username) + time.sleep(5) + logging.debug('User \'{0}\' will log out.'.format(self.username)) + process = subprocess.Popen(self.command_logout_user.format(self.username), stdin=None, env=None, cwd=None, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, shell=True) + process.wait() + + def write_to_global_profile(self): + + process = subprocess.Popen(self.command_get_users_currently_login, stdin=None, env=None, cwd=None, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, shell=True) + process.wait() + p_out = process.stdout.read().decode("unicode_escape") + users = [] + + if p_out != None: + users = str(p_out).split('\n') + users.pop() + + if 'root' in users: + users.remove('root') + + logging.debug('Logged-in users: {0}'.format(users)) + + if str(self.today) in self.days: + + if not (self.start_minute < self.current_minute < self.end_minute and self.current_date <= self.last_date): + for user in users: + logging.debug('User \'{0}\' will log out.'.format(user)) + process = subprocess.Popen(self.command_logout_user.format(user), stdin=None, env=None, cwd=None, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, shell=True) + process.wait() + + elif int(self.end_minute) - int(self.current_minute) == int(self.duration): + for user in users: + Util.send_notify('Lider Ahenk', + 'Oturum {0} dakika sonra kapatılacak!'.format(str(self.duration)), + ':0', + user) + pass + else: + for user in users: + Util.send_notify('Lider Ahenk', + 'Oturum şimdi kapatılacak!', + ':0', + user) + logging.debug('User \'{0}\' will log out.'.format(user)) + process = subprocess.Popen(self.command_logout_user.format(user), stdin=None, env=None, cwd=None, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, shell=True) + process.wait() + + +check = CheckTime() +check.handle() diff --git a/src/plugins/login-manager/scripts/cron.sh b/src/plugins/login-manager/scripts/cron.sh new file mode 100755 index 0000000..8849022 --- /dev/null +++ b/src/plugins/login-manager/scripts/cron.sh @@ -0,0 +1,8 @@ +#!/bin/bash +#write out current crontab +crontab -l > cron +#new cron into cron file +echo "$1" >> cron +#install new cron file +crontab cron +rm cron \ No newline at end of file diff --git a/src/plugins/login-manager/shutdown.py b/src/plugins/login-manager/shutdown.py new file mode 100644 index 0000000..b5de945 --- /dev/null +++ b/src/plugins/login-manager/shutdown.py @@ -0,0 +1,23 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Shutdown(AbstractPlugin): + def __init__(self, context): + super(Shutdown, self).__init__() + self.context = context + self.logger = self.get_logger() + + self.logger.debug('Parameters were initialized.') + + def handle_shutdown_mode(self): + self.logger.debug('Removing login-manager cron job...') + self.execute('crontab -l | sed \'/{0}/d\' | crontab -'.format('login-manager/scripts/check.py')) + + +def handle_mode(context): + shutdown = Shutdown(context) + #shutdown.handle_shutdown_mode() diff --git a/src/plugins/manage-root/init.py b/src/plugins/manage-root/init.py new file mode 100644 index 0000000..f8162b3 --- /dev/null +++ b/src/plugins/manage-root/init.py @@ -0,0 +1,20 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Init(AbstractPlugin): + def __init__(self, context): + super(Init, self).__init__() + self.context = context + self.logger = self.get_logger() + + def handle_mode(self): + # TODO Do what do you want to do! + pass + + +def handle_mode(context): + init = Init(context) + init.handle_mode() diff --git a/src/plugins/manage-root/login.py b/src/plugins/manage-root/login.py new file mode 100644 index 0000000..5e8119c --- /dev/null +++ b/src/plugins/manage-root/login.py @@ -0,0 +1,21 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Login(AbstractPlugin): + def __init__(self, context): + super(Login, self).__init__() + self.context = context + self.username = str(context.get_username()) + self.logger = self.get_logger() + + def handle_mode(self): + # TODO Do what do you want to do! + pass + + +def handle_mode(context): + login = Login(context) + login.handle_mode() diff --git a/src/plugins/manage-root/logout.py b/src/plugins/manage-root/logout.py new file mode 100644 index 0000000..6c17985 --- /dev/null +++ b/src/plugins/manage-root/logout.py @@ -0,0 +1,21 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Logout(AbstractPlugin): + def __init__(self, context): + super(Logout, self).__init__() + self.context = context + self.username = str(context.get_username()) + self.logger = self.get_logger() + + def handle_mode(self): + # TODO Do what do you want to do! + pass + + +def handle_mode(context): + logout = Logout(context) + logout.handle_mode() diff --git a/src/plugins/manage-root/main.py b/src/plugins/manage-root/main.py new file mode 100644 index 0000000..de4527a --- /dev/null +++ b/src/plugins/manage-root/main.py @@ -0,0 +1,5 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +def info(): + return None diff --git a/src/plugins/manage-root/policy.py b/src/plugins/manage-root/policy.py new file mode 100644 index 0000000..56c91da --- /dev/null +++ b/src/plugins/manage-root/policy.py @@ -0,0 +1,23 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Sample(AbstractPlugin): + def __init__(self, profile_data, context): + super(Sample, self).__init__() + self.profile_data = profile_data + self.context = context + self.logger = self.get_logger() + + def handle_policy(self): + # TODO Do what do you want to do! + # TODO Don't Forget returning response with + pass + + +def handle_policy(profile_data, context): + print('Sample Plugin Policy') + sample = Sample(profile_data, context) + sample.handle_policy() diff --git a/src/plugins/manage-root/safe.py b/src/plugins/manage-root/safe.py new file mode 100644 index 0000000..2debd2e --- /dev/null +++ b/src/plugins/manage-root/safe.py @@ -0,0 +1,21 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Safe(AbstractPlugin): + def __init__(self, context): + super(Safe, self).__init__() + self.context = context + self.username = str(context.get_username()) + self.logger = self.get_logger() + + def handle_safe_mode(self): + # TODO Do what do you want to do! + pass + + +def handle_mode(context): + safe = Safe(context) + safe.handle_safe_mode() diff --git a/src/plugins/manage-root/set_root_password.py b/src/plugins/manage-root/set_root_password.py new file mode 100644 index 0000000..3d5c9e1 --- /dev/null +++ b/src/plugins/manage-root/set_root_password.py @@ -0,0 +1,120 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +# Author: Tuncay Çolak +# Author: Hasan Kara + +import subprocess +from base.plugin.abstract_plugin import AbstractPlugin +from base.model.enum.content_type import ContentType +import json +import datetime + +class RootPassword(AbstractPlugin): + def __init__(self, task, context): + super(RootPassword, self).__init__() + self.task = task + self.context = context + self.message_code = self.get_message_code() + self.logger = self.get_logger() + self.create_shadow_password = 'mkpasswd {}' + self.change_password = 'usermod -p {0} {1}' + self.username= 'root' + + + def save_mail(self, status): + cols = ['command', 'mailstatus', 'timestamp']; + values = ['set_root_password', status, self.timestamp()] + self.db_service.update('mail', cols, values) + + def set_mail(self,mail_content): + if mail_content.__contains__('{date}'): + mail_content = str(mail_content).replace('{date}', str(datetime.date.today())); + if mail_content.__contains__('{ahenk}'): + mail_content = str(mail_content).replace('{ahenk}', str(self.Ahenk.dn())); + + self.context.set_mail_content(mail_content) + + def handle_task(self): + lockRootUser = self.task['lockRootUser'] + password = self.task['RootPassword'] + rootEntity = self.task['rootEntity'] + + self.logger.debug('[Root Pass] password: ' + str("**********")) + + mail_send = False + mail_subject = '' + mail_content = '' + + if 'mailSend' in self.task: + mail_send = self.task['mailSend']; + if 'mailSubject' in self.task: + mail_subject = self.task['mailSubject']; + if 'mailContent' in self.task: + mail_content = self.task['mailContent']; + try: + if lockRootUser: + self.logger.info("Locking root user") + result_code, p_out, p_err = self.execute_command("passwd -l root") + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Root kullanıcısı başarıyla kilitlendi.', + data=json.dumps({'Result': p_out}), + content_type=self.get_content_type().APPLICATION_JSON.value) + else: + if str(password).strip() != '': + result_code, p_out, p_err = self.execute_command(self.create_shadow_password.format(password)) + shadow_password = p_out.strip() + self.execute_command(self.change_password.format('\'{}\''.format(shadow_password), self.username)) + self.set_mail(mail_content) + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Parola Başarı ile değiştirildi.', + data=json.dumps({ + 'Result': 'Parola Başarı ile değiştirildi.', + 'mail_content': str(self.context.get_mail_content()), + 'mail_subject': str(self.context.get_mail_subject()), + 'mail_send': self.context.is_mail_send(), + 'rootEntity': rootEntity + }), + content_type=ContentType.APPLICATION_JSON.value) + self.logger.debug('Changed password.') + + except Exception as e: + self.logger.error('Error: {0}'.format(str(e))) + mail_content = 'Root Parolası değiştirlirken hata oluştu.' + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Parola değiştirilirken hata oluştu.', + data=json.dumps({ + 'Result': 'Parola değiştirilirken hata oluştu.', + 'mail_content': str(self.context.get_mail_content()), + 'mail_subject': str(self.context.get_mail_subject()), + 'mail_send': self.context.is_mail_send(), + 'rootEntity': rootEntity + }), + content_type=ContentType.APPLICATION_JSON.value) + + ## this methode is only for manage-root password plugin + def execute_command(self, command, stdin=None, env=None, cwd=None, shell=True, result=True): + + try: + process = subprocess.Popen(command, stdin=stdin, env=env, cwd=cwd, stderr=subprocess.PIPE, + stdout=subprocess.PIPE, shell=shell) + + self.logger.debug('Executing command for manage-root') + + if result is True: + result_code = process.wait() + p_out = process.stdout.read().decode("unicode_escape") + p_err = process.stderr.read().decode("unicode_escape") + + return result_code, p_out, p_err + else: + return None, None, None + except Exception as e: + return 1, 'Could not execute command: {0}. Error Message: {1}'.format(command, str(e)), '' + + + + +def handle_task(task, context): + clz = RootPassword(task, context) + clz.handle_task() diff --git a/src/plugins/manage-root/shutdown.py b/src/plugins/manage-root/shutdown.py new file mode 100644 index 0000000..cda1972 --- /dev/null +++ b/src/plugins/manage-root/shutdown.py @@ -0,0 +1,20 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Shutdown(AbstractPlugin): + def __init__(self, context): + super(Shutdown, self).__init__() + self.context = context + self.logger = self.get_logger() + + def handle_mode(self): + # TODO Do what do you want to do! + pass + + +def handle_mode(context): + shutdown = Shutdown(context) + shutdown.handle_mode() diff --git a/src/plugins/network-inventory/installahenk.py b/src/plugins/network-inventory/installahenk.py new file mode 100644 index 0000000..a6965bb --- /dev/null +++ b/src/plugins/network-inventory/installahenk.py @@ -0,0 +1,114 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import os +import urllib.request + +import paramiko + +from base.plugin.abstract_plugin import AbstractPlugin + + +class InstallAhenk(AbstractPlugin): + def __init__(self, task, context): + super(AbstractPlugin, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.access_method = self.task['accessMethod'] + self.install_method = self.task['installMethod'] + self.ipList = self.task['ipList'] + self.username = self.task['username'] + + if self.access_method == 'USERNAME_PASSWORD': + self.password = self.task['password'] + elif self.access_method == 'PRIVATE_KEY': + self.passphrase = self.task['passphrase'] + self.key_path = self.task['privateKeyPath'] + + if self.install_method == 'APT_GET': + self.install_command = 'sudo apt-get install -y --force-yes ahenk' # TODO name for ahenk + + elif self.install_method == 'WGET': + self.download_url = self.task['downloadUrl'] + + self.deb_path = '/tmp/ahenk.deb' + self.command = 'gdebi -n {}'.format(self.deb_path) + + self.logger.debug('Initialized') + + def handle_task(self): + try: + if self.access_method == 'USERNAME_PASSWORD': + for i, val in enumerate(self.ipList): + self.use_username_password(val) + elif self.access_method == 'PRIVATE_KEY': + for i, val in enumerate(self.ipList): + self.use_key(val) + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='NETWORK INVENTORY görevi başarıyla çalıştırıldı.') + self.logger.info('NETWORK INVENTORY task is handled successfully') + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK INVENTORY task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK INVENTORY görevi çalıştırılırken bir hata oluştu.') + + def use_username_password(self, host): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect(host, username=self.username, password=self.password) + + transport = ssh.get_transport() + session = transport.open_session() + session.set_combine_stderr(True) + session.get_pty() + + self.logger.debug('SSH connection has been started.') + + if self.install_method == 'WGET': + urllib.request.urlretrieve(self.download_url, self.deb_path) + sftp = ssh.open_sftp() + sftp.put(self.deb_path, self.deb_path) + session.exec_command(self.command) + stdout = session.makefile('rb', -1) + + elif self.install_method == 'APT_GET': + session.exec_command(self.install_command) + stdin = session.makefile('wb', -1) + stdout = session.makefile('rb', -1) + stdin.write(self.password + '\n') + stdin.flush() + + self.logger.debug('Ahenk has been installed.') + + def use_key(self, host): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + privatekeyfile = os.path.expanduser(self.key_path) + key = paramiko.RSAKey.from_private_key_file(privatekeyfile) + ssh.connect(host, username=self.username, pkey=key, password=self.passphrase) + + self.logger.debug('SSH connection has been started.') + + if self.install_method == 'WGET': + urllib.request.urlretrieve(self.download_url, self.deb_path) + sftp = ssh.open_sftp() + sftp.put(self.deb_path, self.deb_path) + stdin, stdout, stderr = ssh.exec_command(self.command) + + elif self.install_method == 'APT_GET': + stdin, stdout, stderr = ssh.exec_command(self.install_command) + # TODO need to write user password (because of sudo command) + + self.logger.debug('Ahenk has been installed.') + + +def handle_task(task, context): + install = InstallAhenk(task, context) + install.handle_task() diff --git a/src/plugins/network-inventory/main.py b/src/plugins/network-inventory/main.py new file mode 100644 index 0000000..664b767 --- /dev/null +++ b/src/plugins/network-inventory/main.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +def info(): + inf = dict() + inf['name'] = 'network-inventory' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = '' + inf['task'] = True + inf['user_oriented'] = False + inf['machine_oriented'] = False + inf['developer'] = '' + + return inf \ No newline at end of file diff --git a/src/plugins/network-inventory/multiple-file-transfer.py b/src/plugins/network-inventory/multiple-file-transfer.py new file mode 100644 index 0000000..dc96730 --- /dev/null +++ b/src/plugins/network-inventory/multiple-file-transfer.py @@ -0,0 +1,117 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Caner Feyzullahoglu +""" +Style Guide is PEP-8 +https://www.python.org/dev/peps/pep-0008/ +""" + +from base.plugin.abstract_plugin import AbstractPlugin + +class GetFile(AbstractPlugin): + def __init__(self, task, context): + super(AbstractPlugin, self).__init__() + + self.logger = self.get_logger() + self.logger.debug('Initialized') + + self.task = task + self.context = context + self.message_code = self.get_message_code() + + def handle_task(self): + parameter_map = self.task + self.logger.debug('Handling task') + self.logger.debug('Fetching file from: {0} to {1}'.format(parameter_map['remotePath'], + parameter_map['localPath'])) + + try: + self.context.fetch_file(parameter_map['remotePath'], local_path=parameter_map['localPath'], + file_name=parameter_map['fileName']) + + for param in parameter_map: + if param == 'editUserPermissions': + permissions = '' + if parameter_map['editUserPermissions']: + permissions += 'u+r' if parameter_map['readUser'] else 'u-r' + permissions += '+w' if parameter_map['writeUser'] else '-w' + permissions += '+x' if parameter_map['executeUser'] else '-x' + if parameter_map['ownerUser']: + chown_command = 'chown ' + parameter_map['ownerUser'] + ': "' + parameter_map['localPath'] \ + + parameter_map['fileName'] + '"' + self.logger.debug('Executing chown: ' + chown_command) + result_code, p_out, p_err = self.execute(chown_command) + if result_code != 0: + self.logger.error('Error occurred while executing chown command') + self.logger.error('Error message: {0}'.format(str(p_err))) + warning_message = 'Dosyanın sahibi değiştirilemedi, ' \ + + parameter_map[ + 'ownerUser'] + ' kullanıcısının varolduğundan emin olun. ' + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message=warning_message) + return + else: + if permissions: + self.execute_command(permissions, parameter_map) + + elif param == 'editGroupPermissions': + permissions = '' + if parameter_map['editGroupPermissions']: + permissions += 'g+r' if parameter_map['readGroup'] else 'g-r' + permissions += '+w' if parameter_map['writeGroup'] else '-w' + permissions += '+x' if parameter_map['executeGroup'] else '-x' + if parameter_map['ownerGroup']: + chown_command = 'chown ' + ':' + parameter_map['ownerGroup'] + ' "' \ + + parameter_map['localPath'] \ + + parameter_map['fileName'] + '"' + self.logger.debug('Executing chown: ' + chown_command) + result_code, p_out, p_err = self.execute(chown_command) + if result_code != 0: + self.logger.error('Error occurred while executing chown command') + self.logger.error('Error message: {0}'.format(str(p_err))) + warning_message = 'Dosyanın sahibi değiştirilemedi, ' \ + + parameter_map['ownerGroup'] + ' grubunun varolduğundan emin olun. ' + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message=warning_message) + return + else: + if permissions: + self.execute_command(permissions, parameter_map) + + elif param == 'editOtherPermissions': + permissions = '' + if parameter_map['editOtherPermissions']: + permissions += 'o+r' if parameter_map['readOther'] else 'o-r' + permissions += '+w' if parameter_map['writeOther'] else '-w' + permissions += '+x' if parameter_map['executeOther'] else '-x' + if permissions: + self.execute_command(permissions, parameter_map) + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Dosya transferi başarıyla gerçekleştirildi') + + except Exception as e: + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK INVENTORY Dosya transferi gerçekleştirilirken hata oluştu: ' + str(e)) + + def execute_command(self, permissions, parameter_map): + chmod_command = 'chmod ' + permissions + ' "' + parameter_map['localPath'] \ + + parameter_map['fileName'] + '"' + self.logger.debug('Executing chmod: ' + chmod_command) + result_code, p_out, p_err = self.execute(chmod_command) + if result_code != 0: + self.logger.error('Error occurred while executing chmod command') + self.logger.error('Error message: {0}'.format(str(p_err))) + warning_message = 'Dosyanın hakları değiştirilirken hata oluştu.' + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message=warning_message) + return + else: + self.logger.info('Dosya hakları başarılıyla düzenlendi.') + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message="Dosya hakları başarılıyla düzenlendi") + + +def handle_task(task, context): + scan = GetFile(task, context) + scan.handle_task() diff --git a/src/plugins/network-inventory/scannetwork.py b/src/plugins/network-inventory/scannetwork.py new file mode 100644 index 0000000..2edb46a --- /dev/null +++ b/src/plugins/network-inventory/scannetwork.py @@ -0,0 +1,188 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN +# Author: Caner Feyzullahoglu +""" +Style Guide is PEP-8 +https://www.python.org/dev/peps/pep-0008/ +""" + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class ScanNetwork(AbstractPlugin): + def __init__(self, task, context): + super(AbstractPlugin, self).__init__() + + self.logger.debug('Initialized') + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.logger.debug('Creating nmap command') + uuid = self.generate_uuid() + self.file_path = self.Ahenk.received_dir_path() + uuid + self.command = self.get_nmap_command() + + def handle_task(self): + self.logger.debug('Handling task') + try: + self.logger.debug('Executing command: {0}'.format(self.command)) + result_code, p_out, p_err = self.execute(self.command) + + if result_code != 0: + self.logger.error('Error occurred while executing nmap command') + self.logger.error('Error message: {0}'.format(str(p_err))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK INVENTORY Nmap komutu çalıştırılırken hata oluştu') + else: + self.logger.debug('Nmap command successfully executed') + + data = {} + self.logger.debug('Getting md5 of file') + md5sum = self.get_md5_file(str(self.file_path)) + + self.logger.debug('{0} renaming to {1}'.format(self.file_path, md5sum)) + self.rename_file(self.file_path, self.Ahenk.received_dir_path() + md5sum) + self.logger.debug('Renamed file.') + + data['md5'] = md5sum + + self.logger.debug('Creating response message') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='NETWORK INVENTORY görevi başarıyla çalıştırıldı.', + data=json.dumps(data), + content_type=self.get_content_type().TEXT_PLAIN.value) + + self.logger.info('NETWORK INVENTORY task is handled successfully') + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK INVENTORY task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK INVENTORY görevi çalıştırılırken bir hata oluştu.') + + def get_result(self, root): + self.logger.debug('Parsing nmap xml output') + result_list = {} + index = 1 + + for host in root.findall('host'): + result = {} + + host_names = host.find('hostnames') + ports = host.find('ports') + os = host.find('os') + distance = host.find('distance') + status = host.find('status') + self.logger.debug('STATUS: ++++++ ' + str(status)) + + self.logger.debug('Getting hostname list') + result['hostnames'] = self.get_hostname_list(host_names) + self.logger.debug('Getting port list') + result['ports'] = self.get_port_list(ports) + self.logger.debug('Getting os list') + result['os'] = self.get_os_list(os) + self.logger.debug('Getting distance list') + result['distance'] = self.get_distance(distance) + self.logger.debug('Getting IP, MAC and MAC provider list') + result['ipAddress'], result['macAddress'], result['macProvider'] = self.get_addresses(host) + self.logger.debug('Getting status') + result['status'] = self.get_status(host) + + result_list[index] = result + index += 1 + + return result_list + + def get_addresses(self, host): + ip_address = '' + mac_address = '' + mac_provider = '' + if host is not None: + for address in host.findall('address'): + if address.get('addrtype') == 'ipv4': + ip_address = address.get('addr') + if address.get('addrtype') == 'mac': + mac_address = address.get('addr') + mac_provider = address.get('vendor') + return ip_address, mac_address, mac_provider + + def get_hostname_list(self, hostnames): + hostname_list = '' + if hostnames is not None: + for hostname in hostnames.findall('hostname'): + name = hostname.get('name') + if hostname_list != '': + hostname_list = hostname_list + ', ' + name + else: + hostname_list = name + + return hostname_list + + def get_port_list(self, ports): + port_list = '' + if ports is not None: + for port in ports.findall('port'): + service = port.find('service') + service_name = service.get('name') + port_id = port.get('portid') + '/' + port.get('protocol') + ' ' + service_name + if port_list != '': + port_list = port_list + ', ' + port_id + else: + port_list = port_id + + return port_list + + def get_status(self, host): + state = False + if host is not None: + for status in host.findall('status'): + if status.get('state') == 'up': + state = True + if status.get('state') == 'down': + state = False + + return state + + def get_os_list(self, os): + os_list = '' + if os is not None: + for os_match in os.findall('osmatch'): + name = os_match.get('name') + if os_list != '': + os_list = os_list + ', ' + name + else: + os_list = name + + return os_list + + def get_distance(self, distance): + if distance is not None: + return distance.get('value') + return '' + + def get_nmap_command(self): + command = 'nmap -v -oX' + command += ' - ' + self.task['ipRange'] + if self.task['timingTemplate']: + command += ' -T' + str(self.task['timingTemplate']) + else: + # average speed + command += ' -T3' + + if self.task['ports']: + command += ' -p' + self.task['ports'] + else: + command += ' --top-ports 10' + + command += ' > ' + self.file_path + + return command + + +def handle_task(task, context): + scan = ScanNetwork(task, context) + scan.handle_task() diff --git a/src/plugins/network-manager/add_dns.py b/src/plugins/network-manager/add_dns.py new file mode 100644 index 0000000..f925346 --- /dev/null +++ b/src/plugins/network-manager/add_dns.py @@ -0,0 +1,48 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class AddDNS(AbstractPlugin): + def __init__(self, task, context): + super(AddDNS, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.dns_file = '/etc/resolv.conf' + + self.ip = self.task['ip'] + self.is_active = self.task['is_active'] + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + if self.is_active is True: + content = 'nameserver {}\n'.format(self.ip) + self.logger.debug('Created active dns content.') + else: + content = '#nameserver {}\n'.format(self.ip) + self.logger.debug('Created passive dns content.') + + self.logger.debug('Writing to file...') + self.write_file(self.dns_file, content, 'a') + + self.logger.info('NETWORK-MANAGER - ADD_DNS task is handled successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='DNS bilgisi başarıyla eklendi.') + + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK-MANAGER task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK-MANAGER görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + dns = AddDNS(task, context) + dns.handle_task() diff --git a/src/plugins/network-manager/add_domain.py b/src/plugins/network-manager/add_domain.py new file mode 100644 index 0000000..2da5deb --- /dev/null +++ b/src/plugins/network-manager/add_domain.py @@ -0,0 +1,42 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class AddDomain(AbstractPlugin): + def __init__(self, task, context): + super(AddDomain, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.dns_file = '/etc/resolv.conf' + + self.domain = self.task['domain'] + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + content = 'domain {0}\nsearch {0}\n'.format(self.domain) + + self.logger.debug('Writing to file...') + self.write_file(self.dns_file, content, 'a') + + self.logger.info('NETWORK-MANAGER - ADD_DOMAIN task is handled successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Alan adı bilgisi başarıyla eklendi.') + + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK-MANAGER task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK-MANAGER görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + domain = AddDomain(task, context) + domain.handle_task() diff --git a/src/plugins/network-manager/add_host.py b/src/plugins/network-manager/add_host.py new file mode 100644 index 0000000..37f1687 --- /dev/null +++ b/src/plugins/network-manager/add_host.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class AddHost(AbstractPlugin): + def __init__(self, task, context): + super(AddHost, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.hosts_file = '/etc/hosts' + + self.ip = self.task['ip'] + self.hostname = self.task['hostname'] + self.is_active = self.task['is_active'] + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + if self.is_active is True: + content = '{0} {1}\n'.format(self.ip, self.hostname) + self.logger.debug('Created active host content.') + else: + content = '#{0} {1}\n'.format(self.ip, self.hostname) + self.logger.debug('Created passive host content.') + + self.logger.debug('Writing to file...') + self.write_file(self.hosts_file, content, 'a') + + self.logger.info('NETWORK-MANAGER - ADD_HOST task is handled successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Sunucu bilgisi başarıyla eklendi.') + + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK-MANAGER task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK-MANAGER görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + host = AddHost(task, context) + host.handle_task() diff --git a/src/plugins/network-manager/add_network.py b/src/plugins/network-manager/add_network.py new file mode 100644 index 0000000..783d7c5 --- /dev/null +++ b/src/plugins/network-manager/add_network.py @@ -0,0 +1,73 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class AddNetwork(AbstractPlugin): + def __init__(self, task, context): + super(AddNetwork, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.nic_file = '/etc/network/interfaces' + + self.type = self.task['type'] + self.name = self.task['name'] + self.is_active = self.task['is_active'] + + if self.type == 'STATIC': + self.ip = self.task['ip'] + self.netmask = self.task['netmask'] + self.gateway = self.task['gateway'] + + self.content = '' + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + if self.type == 'STATIC': + if self.is_active is True: + self.content = 'iface {0} inet static\n address {1}\n netmask {2}\n gateway {3}\n'.format(self.name, + self.ip, + self.netmask, + self.gateway) + else: + self.content = 'iface {0} inet static\n#address {1}\n#netmask {2}\n#gateway {3}\n'.format(self.name, + self.ip, + self.netmask, + self.gateway) + + self.logger.debug('Created content for STATIC type.') + elif self.type == 'DHCP': + self.content = 'iface {} inet dhcp\n'.format(self.name) + self.logger.debug('Created content for DHCP type.') + elif self.type == 'LOOPBACK': + self.content = 'iface {} inet loopback\n'.format(self.name) + self.logger.debug('Created content for LOOPBACK type.') + + if self.is_active is False: + self.logger.debug('Network interface is not active.') + self.content = '#{}'.format(self.content) + + self.logger.debug('Writing to file...') + self.write_file(self.nic_file, self.content, 'a') + + self.logger.info('NETWORK-MANAGER - ADD_NETWORK task is handled successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Yeni ağ arayüzü başarıyla eklendi.') + + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK-MANAGER task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK-MANAGER görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + network = AddNetwork(task, context) + network.handle_task() diff --git a/src/plugins/network-manager/allow_port.py b/src/plugins/network-manager/allow_port.py new file mode 100644 index 0000000..74af42e --- /dev/null +++ b/src/plugins/network-manager/allow_port.py @@ -0,0 +1,41 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + +class AddDomain(AbstractPlugin): + def __init__(self, task, context): + super(AddDomain, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.ports = self.task['ports'] + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + self.logger.debug('Writing to iptables ...') + + for port in self.ports.split(' '): + self.execute('iptables -D INPUT -p tcp --dport ' + port + ' -m state --state NEW,ESTABLISHED -j DROP') + self.execute('iptables -D OUTPUT -p tcp --dport ' + port + ' -m state --state NEW,ESTABLISHED -j DROP') + self.execute('iptables-save') + + self.logger.info('NETWORK-MANAGER - Allowing to Ports: ' + self.ports + ' are handled successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Portlar başarıyla açıldı') + + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK-MANAGER task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK-MANAGER görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + domain = AddDomain(task, context) + domain.handle_task() diff --git a/src/plugins/network-manager/block_port.py b/src/plugins/network-manager/block_port.py new file mode 100644 index 0000000..ca590fc --- /dev/null +++ b/src/plugins/network-manager/block_port.py @@ -0,0 +1,41 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + +class AddDomain(AbstractPlugin): + def __init__(self, task, context): + super(AddDomain, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.ports = self.task['ports'] + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + self.logger.debug('Writing to iptables ...') + for port in self.ports.split(' '): + self.execute('iptables -A INPUT -p tcp --dport ' + port + ' -m state --state NEW,ESTABLISHED -j DROP') + self.execute('iptables -A OUTPUT -p tcp --dport ' + port + ' -m state --state NEW,ESTABLISHED -j DROP') + + self.execute('iptables-save') + + self.logger.info('NETWORK-MANAGER - Blocking Ports: ' + self.ports + ' are handled successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Portlar başarıyla engellendi') + + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK-MANAGER task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK-MANAGER görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + domain = AddDomain(task, context) + domain.handle_task() diff --git a/src/plugins/network-manager/change_hostname.py b/src/plugins/network-manager/change_hostname.py new file mode 100644 index 0000000..54f8288 --- /dev/null +++ b/src/plugins/network-manager/change_hostname.py @@ -0,0 +1,42 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class ChangeHostname(AbstractPlugin): + def __init__(self, task, context): + super(ChangeHostname, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.hostname_file = '/etc/hostname' + + self.hostname = self.task['hostname'] + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + content = '{}\n'.format(self.hostname) + + self.logger.debug('[NETWORK-MANAGER - ADD_HOST] Writing to file...') + self.write_file(self.hostname_file, content) + + self.logger.info('NETWORK-MANAGER - CHANGE_HOSTNAME task is handled successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Sunucu ismi başarıyla değiştirildi.') + + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK-MANAGER task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK-MANAGER görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + hostname = ChangeHostname(task, context) + hostname.handle_task() diff --git a/src/plugins/network-manager/delete_dns.py b/src/plugins/network-manager/delete_dns.py new file mode 100644 index 0000000..71548f9 --- /dev/null +++ b/src/plugins/network-manager/delete_dns.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import re + +from base.plugin.abstract_plugin import AbstractPlugin + + +class DeleteDNS(AbstractPlugin): + def __init__(self, task, context): + super(DeleteDNS, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.dns_file = '/etc/resolv.conf' + + self.ip = self.task['ip'] + self.is_active = self.task['is_active'] + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + + lines = self.read_file_by_line(self.dns_file) + f = open(self.dns_file, "w") + + for line in lines: + line = str(line).strip(" ") + # to remove multiple spaces + line = re.sub(' +', ' ', line) + + if self.is_active is True: + if line != 'nameserver {}\n'.format(self.ip): + self.logger.debug( + 'Writing a line to dns file... Line: {}'.format(line)) + f.write(line) + else: + self.logger.debug('Line has been deleted from dns file. Line: {}'.format(line)) + else: + if line != '#nameserver {}\n'.format(self.ip) and line != '# nameserver {}\n'.format(self.ip): + self.logger.debug( + 'Writing a line to dns file... Line: {}'.format(line)) + f.write(line) + else: + self.logger.debug( + 'Line has been deleted from dns file. Line: {}'.format(line)) + + f.close() + + self.logger.info('NETWORK-MANAGER - DELETE_DNS task is handled successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='DNS bilgisi başarıyla silindi.') + + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK-MANAGER task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK-MANAGER görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + dns = DeleteDNS(task, context) + dns.handle_task() diff --git a/src/plugins/network-manager/delete_domain.py b/src/plugins/network-manager/delete_domain.py new file mode 100644 index 0000000..46faa8b --- /dev/null +++ b/src/plugins/network-manager/delete_domain.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import re + +from base.plugin.abstract_plugin import AbstractPlugin + + +class DeleteDomain(AbstractPlugin): + def __init__(self, task, context): + super(DeleteDomain, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.dns_file = '/etc/resolv.conf' + + self.domain = self.task['domain'] + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + lines = self.read_file_by_line(self.dns_file) + f = open(self.dns_file, "w") + + for line in lines: + line = str(line).strip(" ") + # to remove multiple spaces + line = re.sub(' +', ' ', line) + + if line != 'domain {}\n'.format(self.domain) and line != 'search {}\n'.format(self.domain): + self.logger.debug( + 'Writing a line to dns file... Line: {}'.format(line)) + f.write(line) + else: + self.logger.debug( + 'Line has been deleted from dns file. Line: {}'.format(line)) + + f.close() + + self.logger.info('NETWORK-MANAGER - DELETE_DOMAIN task is handled successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Alan adı bilgisi başarıyla silindi.') + + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK-MANAGER task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK-MANAGER görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + domain = DeleteDomain(task, context) + domain.handle_task() diff --git a/src/plugins/network-manager/delete_host.py b/src/plugins/network-manager/delete_host.py new file mode 100644 index 0000000..963351d --- /dev/null +++ b/src/plugins/network-manager/delete_host.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import re + +from base.plugin.abstract_plugin import AbstractPlugin + + +class DeleteHost(AbstractPlugin): + def __init__(self, task, context): + super(DeleteHost, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.hosts_file = '/etc/hosts' + + self.ip = self.task['ip'] + self.hostname = self.task['hostname'] + self.is_active = self.task['is_active'] + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + + lines = self.read_file_by_line(self.hosts_file) + f = open(self.hosts_file, "w") + + for line in lines: + line = str(line).strip(" ") + # to remove multiple spaces + line = re.sub(' +', ' ', line) + + if self.is_active is True: + if line != '{0} {1}\n'.format(self.ip, self.hostname): + self.logger.debug( + 'Writing a line to hosts file... Line: {}'.format(line)) + f.write(line) + else: + self.logger.debug( + 'Line has been deleted from hosts file. Line: {}'.format(line)) + else: + if line != '#{0} {1}\n'.format(self.ip, self.hostname) and line != '# {0} {1}\n'.format(self.ip, + self.hostname): + self.logger.debug( + 'Writing a line to hosts file... Line: {}'.format(line)) + f.write(line) + else: + self.logger.debug( + 'Line has been deleted from hosts file. Line: {}'.format(line)) + + f.close() + + self.logger.info('NETWORK-MANAGER - DELETE_HOST task is handled successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Sunucu bilgisi başarıyla silindi.') + + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK-MANAGER task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK-MANAGER görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + host = DeleteHost(task, context) + host.handle_task() diff --git a/src/plugins/network-manager/delete_network.py b/src/plugins/network-manager/delete_network.py new file mode 100644 index 0000000..762a0da --- /dev/null +++ b/src/plugins/network-manager/delete_network.py @@ -0,0 +1,87 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import fileinput +import re + +from base.plugin.abstract_plugin import AbstractPlugin + + +class DeleteNetwork(AbstractPlugin): + def __init__(self, task, context): + super(DeleteNetwork, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.nic_file = '/etc/network/interfaces' + + self.type = self.task['type'] + self.name = self.task['name'] + self.is_active = self.task['is_active'] + + if self.type == 'STATIC': + self.ip = self.task['ip'] + self.netmask = self.task['netmask'] + self.gateway = self.task['gateway'] + + self.content = '' + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + counter = 0 + for line in fileinput.input(self.nic_file, inplace=True): + line = str(line).strip(" ") + # to remove multiple spaces + line = re.sub(' +', ' ', line) + + if not counter: + if self.type == 'static': + if self.is_active is True: + self.content = 'iface {0} inet static\n'.format(self.name) + else: + self.content = '#iface {0} inet static\n'.format(self.name) + + if line.startswith(self.content): + counter = 3 + else: + print(str(line).strip()) + + elif self.type == 'dhcp': + if self.is_active is True: + self.content = 'iface {} inet dhcp\n'.format(self.name) + else: + self.content = '#iface {} inet dhcp\n'.format(self.name) + + if not line.startswith(self.content): + print(str(line).strip()) + + elif self.type == 'loopback': + if self.is_active is True: + self.content = 'iface {} inet loopback\n'.format(self.name) + else: + self.content = '#iface {} inet loopback\n'.format(self.name) + + if not line.startswith(self.content): + print(str(line).strip()) + else: + counter -= 1 + + self.logger.info('NETWORK-MANAGER - DELETE_NETWORK task is handled successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Ağ arayüzü başarıyla silindi.') + + except Exception as e: + self.logger.error( + 'A problem occured while handling NETWORK-MANAGER task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK-MANAGER görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + network = DeleteNetwork(task, context) + network.handle_task() diff --git a/src/plugins/network-manager/get_network_information.py b/src/plugins/network-manager/get_network_information.py new file mode 100644 index 0000000..1de55d5 --- /dev/null +++ b/src/plugins/network-manager/get_network_information.py @@ -0,0 +1,106 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + +class NetworkInformation(AbstractPlugin): + def __init__(self, task, context): + super(NetworkInformation, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.nic_file = '/etc/network/interfaces' + self.hosts_file = '/etc/hosts' + self.dns_file = '/etc/resolv.conf' + self.hostname_file = '/etc/hostname' + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + interfaces = self.read_file(self.nic_file) + self.logger.debug('Read interfaces file.') + + hosts = self.read_file(self.hosts_file) + self.logger.debug('Read hosts file.') + + dns = self.read_file(self.dns_file) + self.logger.debug('Read dns file.') + + machine_hostname = self.read_file(self.hostname_file) + self.logger.debug('Read hostname file.') + + # get ports and ports information + self.logger.debug('Fetch ports information') + result_code, p_out, p_err = self.execute('ss -lntu | grep \'tcp\|udp\' | awk \'{print $1,$5}\'') + result_code_2, p_out_2, p_err_2 = self.execute( + 'cat /etc/services | grep \'/tcp\|/udp\' | awk \'{print $1,$2,$3,$4}\'') + if p_err == "" and p_err_2 == "": + list = "" + for port_line in p_out.splitlines(): + service_name = "" + port_line = str(port_line).split(" ") + port_type = port_line[0] + port_number = port_line[1][(str(port_line[1]).rfind(':') + 1):] + for service_line in p_out_2.splitlines(): + service_line.replace("# ", ""); + service_line = str(service_line).split(" ") + service_port = service_line[1].split("/") + if port_number == service_port[0]: + service_name = service_line[0] + if service_name == "": + service_name = "unknown" + port_info_str = service_name + " " + port_type + " " + port_number + if port_info_str.strip() not in list: + # check if port is blocked or not in iptables + result_code_3, p_out_3, p_err_3 = self.execute('sudo iptables -L') + is_port_blocked_input = False + is_port_blocked_output = False + state = "input" + if p_err_3 == "": + for ip_line in p_out_3.splitlines(): + if ip_line in "Chain INPUT": + state = "input" + if ip_line in "Chain OUTPUT": + state = "output" + + if state == "input" and "DROP" in ip_line and ( + "dpt:" + port_number in ip_line or "dpt:" + service_name in ip_line): + is_port_blocked_input = True + if state == "output" and "DROP" in ip_line and ( + "dpt:" + port_number in ip_line or "dpt:" + service_name in ip_line): + is_port_blocked_output = True + if is_port_blocked_input: + port_info_str += " Blocked" + else: + port_info_str += " Allowed" + if is_port_blocked_output: + port_info_str += " Blocked" + else: + port_info_str += " Allowed" + + list += port_info_str.strip() + "\n" + + port = list.strip() + + self.logger.info('NETWORK-MANAGER task is handled successfully.') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Ağ dosyaları başarıyla okundu.', + data=json.dumps({'interfaces': interfaces, 'hosts': hosts, 'port': port, 'dns': dns, + 'machine_hostname': machine_hostname}), + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.error('A problem occured while handling NETWORK-MANAGER task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='NETWORK-MANAGER görevi uygulanırken bir hata oluştu.') + + +def handle_task(task, context): + ni = NetworkInformation(task, context) + ni.handle_task() diff --git a/src/plugins/network-manager/main.py b/src/plugins/network-manager/main.py new file mode 100644 index 0000000..04e85b2 --- /dev/null +++ b/src/plugins/network-manager/main.py @@ -0,0 +1,15 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +def info(): + inf = dict() + inf['name'] = 'network-manager' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'Network-Manager plugin provides to managing host, domain, dns and other network settings.' + inf['task'] = True + inf['user_oriented'] = False + inf['machine_oriented'] = False + inf['developer'] = 'mine.dogan@agem.com.tr' + + return inf \ No newline at end of file diff --git a/src/plugins/package-manager/check_package.py b/src/plugins/package-manager/check_package.py new file mode 100644 index 0000000..490ceef --- /dev/null +++ b/src/plugins/package-manager/check_package.py @@ -0,0 +1,63 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class CheckPackage(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + def handle_task(self): + try: + package_name = str((self.data)['packageName']) + package_version = str((self.data)['packageVersion']) + dn = self.Ahenk.dn() + res = {} + if dn is None: + dn = " " + result_code, result, p_err = self.execute('dpkg -s {} | grep Version'.format(package_name)) + data = result.split(': ') + self.logger.debug(data) + + if data[0] == 'Version': # Package is installed + if package_version is None or len(package_version) == 0: + result = 'Paket yüklü' + res['version'] = data[1] + elif data[1] is not None and (package_version + '\n') in data[ + 1]: # Package version is the same with wanted version + result = 'Paket yüklü' + res['version'] = data[1] + else: + result = 'Paket yüklü; fakat başka bir versiyonla' + res['version'] = data[1] + else: # Package is not installed + result = 'Paket yüklü değil' + res['version'] = '' + + res["dn"] = dn + res["res"] = result + + self.logger.debug("Result is: - {}".format(result)) + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='{0} - {1}'.format(package_name, result), + data=json.dumps(res), + content_type=self.get_content_type().APPLICATION_JSON.value) + self.logger.debug("Package Info has sent") + except Exception as e: + self.logger.debug(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Paket Bilgilerini transferde beklenmedik hata!', + content_type=self.get_content_type().APPLICATION_JSON.value) + + +def handle_task(task, context): + plugin = CheckPackage(task, context) + plugin.handle_task() diff --git a/src/plugins/package-manager/get_execution_info.py b/src/plugins/package-manager/get_execution_info.py new file mode 100644 index 0000000..6c9f5b3 --- /dev/null +++ b/src/plugins/package-manager/get_execution_info.py @@ -0,0 +1,247 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class GetExecutionInfo(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + self.command_execution_statistic_list = [] + self.version_list = [] + self.result_message = '' + self.commands = [] + self.logger.debug('Execution info initialized') + self.temp_file_name = str(self.generate_uuid()) + self.file_path = '{0}{1}'.format(str(self.Ahenk.received_dir_path()), self.temp_file_name) + self.isRecordExist = 1 + + def handle_task(self): + + self.logger.debug('Task handling') + try: + result_record_number = None + commands = self.data['command'] + user = self.data['user'] + is_strict_match = self.data['isStrictMatch'] + self.create_file(self.file_path) + # if commands fields is not empty + if commands: + if is_strict_match is False: + lastcomm_command = 'lastcomm ' + for command in commands.split(): + lastcomm_command += ' --command {0} '.format(command) + if user: + lastcomm_command += " --user {}".format(user) + self.logger.debug( + ' {0} command will be executed'.format(lastcomm_command)) + result_code, result, error = self.execute(lastcomm_command + ' > /tmp/result.txt') + self.logger.debug( + ' {0} command is executed'.format(lastcomm_command)) + result_record_number = self.check_output(result_code) + else: + for command in commands.split(): + self.logger.debug(command) + lastcomm_command = 'lastcomm --command {0} '.format(command) + if user: + lastcomm_command += " --user {}".format(user) + lastcomm_command += " --strict-match" + self.logger.debug(' {0} command will be executed'.format( + lastcomm_command)) + result_code, result, error = self.execute(lastcomm_command + ' > /tmp/result.txt') + self.logger.debug( + ' {0} command is executed'.format(lastcomm_command)) + result_record_number = self.check_output(result_code) + # if command does not exist and user is exist + elif user: + lastcomm_command = 'lastcomm --user {0} '.format(user) + if is_strict_match is True: + lastcomm_command += ' --strict-match' + self.logger.debug( + ' {0} command will be executed'.format(lastcomm_command)) + result_code, result, error = self.execute(lastcomm_command + ' > /tmp/result.txt') + self.logger.debug( + ' {0} command is executed + result_code = {1}'.format( + lastcomm_command, result_code) + ' > /tmp/result.txt') + result_record_number = self.check_output(result_code) + + # Record not found + if result_record_number is None: + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='İstenilene uygun veri bulunamadı') + elif self.is_exist(self.file_path): + self.execute("sed -i '$ d' " + self.file_path) + self.execute('echo "]," >> ' + self.file_path) + self.execute('echo \\"versionList\\" :[ >> ' + self.file_path) + for command_name in self.commands: + self.check_version_list(command_name) + self.execute('echo "]" >> ' + self.file_path) + self.execute('echo "}" >> ' + self.file_path) + data = {} + md5sum = self.get_md5_file(str(self.file_path)) + self.logger.debug('{0} renaming to {1}'.format(self.temp_file_name, md5sum)) + self.rename_file(self.file_path, self.Ahenk.received_dir_path() + '/' + md5sum) + self.logger.debug('Renamed.') + data['md5'] = md5sum + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Uygulama çalıştırma bilgileri başarıyla sisteme geçirildi.', + data=json.dumps(data), + content_type=self.get_content_type().TEXT_PLAIN.value) + self.logger.debug("Execution Info fetched succesfully. ") + self.logger.debug("Execution Info has sent") + else: + raise Exception('File not found on this path: {}'.format(self.file_path)) + + except Exception as e: + self.logger.debug( + ' Unexpected error in get_execution.py. Error message : {0}'.format( + str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Uygulama çalıştırma bilgilerini getirirken beklenmedik hata!') + + def check_version_list(self, command): + is_exist = False + for version in self.version_list: + if version.commandName == command: + is_exist = True + if not is_exist: + result_code, result, p_err = self.execute('whereis {0}'.format(command)) + if result_code == 0 and "komut yok" not in result and len(result.split(':')) >= 2 and result[ + len(result) - 2] != ':': + self.logger.debug('SON HARF' + str(result[len(result) - 2])) + result = result.split(':')[1] + if result.split() is None or len(result.split()) == 0: + self.logger.debug(' Command installed place is not found') + self.result_message += 'Command {0} could not found'.format(command) + else: + if len(self.version_list) > 0: + self.execute('echo , >> ' + self.file_path) + self.logger.debug(' Command installed place is found') + + self.logger.debug(' Result = {0}'.format(str(result))) + result = result.split() + self.logger.debug(' Result split= {0}'.format(str(result))) + result = result[0] + self.logger.debug( + ' Result split 0 = {0}'.format(str(result))) + + result_code, result, p_err = self.execute('dpkg-query -S {0}'.format(result)) + if result_code == 0: # Command exists + self.logger.debug( + ' Command related package name is found') + result = result.split(': ')[0] + result_code, p_result, p_err = self.execute('dpkg -s {0} | grep Version'.format(result)) + if result_code == 0: + res = p_result.splitlines() + self.logger.debug( + ' Command related package version is found') + t_command = 'echo "{ \\"c\\": \\"' + command + '\\", \\"p\\": \\"' + result + '\\", \\"v\\":\\"' + \ + res[0].split(': ')[1] + '\\"}" >> ' + self.file_path + self.logger.debug( + ' Command is : {0}'.format(t_command)) + self.execute(t_command) + self.logger.debug( + ' Appending to version list') + self.version_list.append(VersionInfoItem(command, result, res[0].split(': ')[1])) + else: + self.logger.debug( + ' Command related package version is not found') + self.result_message += 'Command\'s related package version could not be parsed(Deb : {0}).'.format( + result) + t_command = 'echo "{ \\"c\\": \\"' + command + '\\", \\"p\\": \\"' + result + '\\", \\"v\\":\\" - \\"}" >> ' + self.file_path + self.logger.debug( + ' Command is : {0}'.format(t_command)) + self.execute(t_command) + self.logger.debug( + ' Appending to version list') + self.version_list.append(VersionInfoItem(command, result, '-')) + else: # command not exists + self.logger.debug( + ' Command related package name is not found') + self.result_message += 'Command\'s related package could not be found(Command : {0})'.format( + result) + t_command = 'echo "{ \\"c\\": \\"' + command + '\\", \\"p\\": \\" - \\", \\"v\\":\\" - \\"}" >> ' + self.file_path + self.logger.debug( + ' Command is : {0}'.format(t_command)) + self.execute(t_command) + self.logger.debug( + ' Appending to version list') + self.version_list.append(VersionInfoItem(command, '-', '-')) + else: # command not exists + self.logger.debug(' Command installed place is not found') + self.result_message += 'Command {0} could not found'.format(command) + + def check_output(self, result_code): + try: + if result_code == 0: + self.logger.debug( + ' lastcomm execution has returned with no error') + f = open("/tmp/result.txt", "r") + lines = f.readlines() + i = 0 + for line in lines: + if self.isRecordExist == 1: + self.execute('echo { \\"commandExecutionInfoList\\" :[ >> ' + self.file_path) + self.logger.debug(' line parsing has done') + output_columns = line.split() + self.logger.debug(' Column parsing has done') + command_name = output_columns[0] + user = output_columns[len(output_columns) - 8] + process_time = output_columns[len(output_columns) - 6] + start_date = output_columns[len(output_columns) - 4] + ' ' + output_columns[ + len(output_columns) - 3] + ' ' + output_columns[len(output_columns) - 2] + ' ' + \ + output_columns[len(output_columns) - 1] + self.logger.debug( + ' CommandExecutionInfoItem attributes are ready for adding to result list') + self.execute( + 'echo "{ \\"p\\": \\"' + process_time + '\\", \\"c\\": \\"' + command_name + '\\", \\"u\\":\\"' + user + '\\", \\"s\\":\\"' + start_date + '\\"}" >> ' + self.file_path) + self.logger.debug( + ' CommandExecutionInfoItem is created and added to result list') + self.commands.append(command_name) + self.logger.debug(str(len(lines)) + '------------' + str(i)) + self.execute('echo "," >> ' + self.file_path) + self.isRecordExist += 1 + i += 1 + if i >= 1: + return 'Basarili' + if self.isRecordExist > 0: + return 'Basarili' + return None + else: + self.logger.debug( + ' lastcomm command has not return with a result') + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Uygulama çalıştırma bilgilerini getirirken beklenmedik hata!') + return None + + except Exception as e: + self.logger.debug('[ PACKAGE MANAGER ]Error in check_output method {}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Uygulama çalıştırma bilgilerini getirirken beklenmedik hata!') + + +class VersionInfoItem: + def __init__(self, command_name, package_name, package_version): + self.commandName = command_name + self.packageName = package_name + self.packageVersion = package_version + + +class CommandExecutionInfoItem: + def __init__(self, command_name, user, process_time, start_date): + self.commandName = command_name + self.user = user + self.processTime = process_time + self.startDate = start_date + + +def handle_task(task, context): + plugin = GetExecutionInfo(task, context) + plugin.handle_task() diff --git a/src/plugins/package-manager/init.py b/src/plugins/package-manager/init.py new file mode 100644 index 0000000..acda1aa --- /dev/null +++ b/src/plugins/package-manager/init.py @@ -0,0 +1,46 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Init(AbstractPlugin): + def __init__(self, context): + super(Init, self).__init__() + self.context = context + self.logger = self.get_logger() + + def handle_mode(self): + try: + if self.is_installed('chkconfig') is False: + result_code, result, error = self.install_with_apt_get('chkconfig') + if result_code != 0: + self.logger.error('Package chkconfig can not be installed') + else: + self.logger.debug("[PACKAGE MANAGER -INIT] Package chkconfig installed successfully") + if self.is_installed('acct') is False: + result_code, result, error = self.install_with_apt_get('acct') + if result_code != 0: + self.logger.error("Package acct can not be installed") + else: + self.logger.debug("Package acct installed successfully") + except Exception as e: + self.logger.error('Error while installing chkconfig and acct packages. Error message : {0}'.format(str(e))) + result_code, result, error = self.execute('chkconfig acct on') + try: + if result_code == 0: + result_code, result, error = self.execute('/etc/init.d/acct start') + if result_code == 0: + self.logger.debug('acct service started successfully') + else: + self.logger.error( + 'acct service could not be started - Error while executing /etc/init.d/acct start command') + else: + self.logger.error('chkconfig acct on command could not executed') + except Exception as e: + self.logger.error('Error while starting acct service. Error message : {0}'.format(str(e))) + + +def handle_mode(context): + init = Init(context) + init.handle_mode() diff --git a/src/plugins/package-manager/installed_packages.py b/src/plugins/package-manager/installed_packages.py new file mode 100644 index 0000000..c70cf34 --- /dev/null +++ b/src/plugins/package-manager/installed_packages.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class InstalledPackages(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.temp_file_name = str(self.generate_uuid()) + self.file_path = '{0}{1}'.format(str(self.Ahenk.received_dir_path()), self.temp_file_name) + + def handle_task(self): + try: + self.logger.debug('Executing command for package list.') + self.execute( + 'dpkg-query -f=\'${{Status}},${{binary:Package}},${{Version}}\n\' -W \'*\' | grep \'install ok installed\' | sed \'s/install ok installed/i/\' | sed \'s/unknown ok not-installed/u/\' | sed \'s/deinstall ok config-files/u/\' > {0}'.format( + self.file_path)) + self.logger.debug('Command executed.') + + if self.is_exist(self.file_path): + data = {} + md5sum = self.get_md5_file(str(self.file_path)) + self.logger.debug('{0} renaming to {1}'.format(self.temp_file_name, md5sum)) + self.rename_file(self.file_path, self.Ahenk.received_dir_path() + '/' + md5sum) + self.logger.debug('Renamed.') + data['md5'] = md5sum + json_data = json.dumps(data) + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Paket listesi başarıyla okundu.', + data=json_data, content_type=self.get_content_type().TEXT_PLAIN.value) + self.logger.debug('Package list created successfully') + else: + raise Exception('File not found on this path: {}'.format(self.file_path)) + + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Paket listesi oluşturulurken hata oluştu: ' + str(e), + content_type=self.get_content_type().APPLICATION_JSON.value) + + +def handle_task(task, context): + plugin = InstalledPackages(task, context) + plugin.handle_task() diff --git a/src/plugins/package-manager/main.py b/src/plugins/package-manager/main.py new file mode 100644 index 0000000..d1a9c3c --- /dev/null +++ b/src/plugins/package-manager/main.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +def info(): + inf = dict() + inf['name'] = 'package-manager' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'General repository and package operations (like repo/package installing/uninstalling, searching repo/package...etc)' + inf['task'] = True + inf['user_oriented'] = False + inf['machine_oriented'] = False + inf['developer'] = 'cemre.alpsoy@agem.com.tr' + + return inf diff --git a/src/plugins/package-manager/package_archive.py b/src/plugins/package-manager/package_archive.py new file mode 100644 index 0000000..9e68c56 --- /dev/null +++ b/src/plugins/package-manager/package_archive.py @@ -0,0 +1,44 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +from base.plugin.abstract_plugin import AbstractPlugin + + +class PackageArchive(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + def handle_task(self): + self.logger.debug('Handling Package Archive Task') + try: + resultMessage = '' + package_name = str((self.data)['packageName']) + package_version = str((self.data)['packageVersion']) + self.logger.debug("Installing new package... {0}".format(package_name)) + result_code, p_result, p_err = self.install_with_apt_get(package_name, package_version) + if result_code == 0: + resultMessage += 'Paket başarıyla kuruldu - {0}={1}'.format(package_name, package_version) + self.logger.debug(resultMessage) + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message=resultMessage) + else: + self.logger.debug(resultMessage) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Önceki paket sürümü kurulumunda beklenmedik hata!', + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.debug(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Önceki paket sürümü kurulumunda beklenmedik hata!', + content_type=self.get_content_type().APPLICATION_JSON.value) + + +def handle_task(task, context): + plugin = PackageArchive(task, context) + plugin.handle_task() diff --git a/src/plugins/package-manager/package_management.py b/src/plugins/package-manager/package_management.py new file mode 100644 index 0000000..534170b --- /dev/null +++ b/src/plugins/package-manager/package_management.py @@ -0,0 +1,87 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class PackageManagement(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + def handle_task(self): + try: + items = self.data['packageInfoList'] + result_message = '' + installed_packages = '' + uninstalled_packages = '' + failed_packages = '' + + for item in items: + + # Install package + if item['tag'] == 'i': + self.logger.debug("Installing package: {0}".format(item['packageName'])) + try: + result_code, p_result, p_err = self.install_with_apt_get(item['packageName'], item['version']) + if result_code == 0: + self.logger.debug("Installed package: {0}".format(item['packageName'])) + installed_packages += ' ' + item['packageName'] + else: + self.logger.debug("Couldnt Installed package: {0}".format(item['packageName'])) + failed_packages += ' ' + item['packageName'] + + except Exception as e1: + self.logger.error(str(e1)) + failed_packages += ' ' + item['packageName'] + + # Uninstall package + elif item['tag'] == 'u': + self.logger.debug("Uninstalling package: {0}".format(item['packageName'])) + try: + result_code, p_result, p_err = self.uninstall_package(item['packageName'], item['version']) + if result_code == 0: + self.logger.debug("Uninstalled package: {0}".format(item['packageName'])) + uninstalled_packages += ' ' + item['packageName'] + else: + self.logger.debug( + "Couldnt Uninstalled package: {0}".format(item['packageName'])) + failed_packages += ' ' + item['packageName'] + except Exception as e2: + self.logger.error(str(e2)) + failed_packages += ' ' + item['packageName'] + + # Result message + if installed_packages: + result_message += ' Kurulan paketler: (' + installed_packages + ' )' + if uninstalled_packages: + result_message += ' Kaldırılan paketler: (' + uninstalled_packages + ' )' + if failed_packages: + result_message += ' Hata alan paketler: (' + failed_packages + ' )' + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Paket işlemleri sırasında hata oluştu: ' + result_message, + data=json.dumps({'Result': result_message}), + content_type=self.get_content_type().APPLICATION_JSON.value) + else: + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Paket işlemleri başarıyla gerçekleştirildi: ' + result_message, + data=json.dumps({'Result': result_message}), + content_type=self.get_content_type().APPLICATION_JSON.value) + # TODO return package list! + + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Paket kur/kaldır işlemleri gerçekleştirilirken hata oluştu:' + str(e), + content_type=self.get_content_type().APPLICATION_JSON.value) + + +def handle_task(task, context): + plugin = PackageManagement(task, context) + plugin.handle_task() diff --git a/src/plugins/package-manager/package_sources.py b/src/plugins/package-manager/package_sources.py new file mode 100644 index 0000000..2ca4c31 --- /dev/null +++ b/src/plugins/package-manager/package_sources.py @@ -0,0 +1,109 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +import json +from base.plugin.abstract_plugin import AbstractPlugin +from os import listdir +from os.path import isfile, join + +class PackageSources(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + def handle_task(self): + added_items = self.data['addedItems'] + deleted_items = self.data['deletedItems'] + error_message = "" + try: + # Add desired repositories + for item in added_items: + result_code, p_out, p_err = self.execute( + '/bin/bash {}package-manager/scripts/sourcelist.sh'.format(self.Ahenk.plugins_path())) + + repo_not_found = True + for line in p_out.splitlines(): + if item == line: + repo_not_found = False + break + + if repo_not_found: + file_source_list = open("/etc/apt/sources.list.d/liderahenk.list", 'a+') + file_source_list.write(item + "\n") + file_source_list.close() + + if result_code != 0: + self.logger.error("Error occurred while adding repository: " + str(p_err)) + error_message += " Paket deposu eklenirken hata oluştu: " + str(p_err) + self.logger.debug("Added repositories") + + # Remove desired repositories + for item in deleted_items: + command = 'find /etc/apt/ -name \*.list -type f -exec sed -i \'/' + str(item).replace("/", + "\\/") + '/d\' \{\} \;' + #deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main + is_file_deleted = False + f_name_list = [f for f in listdir("/etc/apt/sources.list.d") if isfile(join("/etc/apt/sources.list.d", f))] + for idx, f_name in enumerate(f_name_list): + f_name_list[idx] = "/etc/apt/sources.list.d/" + f_name + + f_name_list.append("/etc/apt/sources.list") + for f_name in f_name_list: + file_sources = open(f_name, 'r') + file_data = file_sources.read() + file_sources.close() + if item in file_data: + file_data = file_data.replace(item, "") + is_file_deleted = True + file_sources = open(f_name, 'w') + file_sources.write(file_data) + file_sources.close() + break + + #result_code, p_out, p_err = self.execute(command) + + if is_file_deleted is False: + self.logger.error("Error occurred while removing repository: " + str(p_err)) + error_message += " Paket deposu silinirken hata oluştu: " + str(p_err) + self.logger.debug("Removed repositories") + + # Update package lists + self.execute('apt-get update') + self.logger.debug("Updated package lists") + + # Read package repositories + command = '/bin/bash {0}package-manager/scripts/sourcelist.sh'.format(self.Ahenk.plugins_path()) + result_code, p_out, p_err = self.execute(command) + data = {} + + if result_code != 0: + self.logger.error("Error occurred while listing repositories: " + str(p_err)) + error_message += " Paket depoları okunurken hata oluştu: " + str(p_err) + else: + data['packageSource'] = p_out + self.logger.debug("Repositories are listed") + + if not error_message: + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Paket depoları başarıyla güncellendi.', + data=json.dumps(data), + content_type=self.get_content_type().APPLICATION_JSON.value) + else: + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message=error_message, + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.debug(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message="Paket depoları güncellenirken hata oluştu: " + str(e), + content_type=self.get_content_type().APPLICATION_JSON.value) + + +def handle_task(task, context): + plugin = PackageSources(task, context) + plugin.handle_task() diff --git a/src/plugins/package-manager/packages.py b/src/plugins/package-manager/packages.py new file mode 100644 index 0000000..8e13342 --- /dev/null +++ b/src/plugins/package-manager/packages.py @@ -0,0 +1,133 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +import os +from glob import glob + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Packages(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + def handle_task(self): + self.logger.debug('Handling Packages Task') + try: + cn = '{0}\r\n'.format(self.Ahenk.dn().split(',')[0]) + + items = (self.data)['packageInfoList'] + for item in items: + try: + if self.has_attr_json(item, 'tag') and self.has_attr_json(item, 'source'): + array = item['source'].split() + source = ' ' + source = source.join(array) + + ## REPO ADD / CHECK + + self.logger.debug( + "Checking source {0}".format(item['source'])) + + if self.is_repo_exist(source): + self.logger.debug('{0} Source already exists'.format(source)) + else: + self.logger.debug('Source adding...') + try: + self.add_source(source) + except Exception as e: + self.logger.error('Source could not added') + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='{0}\n Kaynaklar eklenmeye çalışırken hata oluştu. Hata Mesajı:{1}'.format( + cn, str(e))) + return + + self.logger.debug('{0} Source added'.format(source)) + + return_code_update, result_update, error_update = self.execute('apt-get update') + if return_code_update == 0: + self.logger.debug('Packages were updated') + else: + self.logger.error('Packages could not updated') + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='{0}\n Kaynaklar güncellenmeye çalışırken hata oluştu. Hata Mesajı: {1}'.format( + cn, str(error_update))) + return + + ## INSTALL/REMOVE PACKAGE + + if item['tag'] == 'Yükle' or item['tag'] == 'Install': + self.logger.debug( + "Installing new package... {0}".format(item['packageName'])) + result_code, p_result, p_err = self.install_with_apt_get(item['packageName'], + item['version']) + if result_code == 0: + self.logger.debug( + "Package installed : {0}={1}".format(item['packageName'], + item['version'])) + else: + self.logger.error( + "Package could not be installed : {0}={1} " + ". Error Message:{2}".format( + item['packageName'], item['version'], str(p_err))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='{0}\n Source eklendi fakat paket kurulurken ' + 'hata oluştu. Hata Mesajı: {1}'.format( + cn, str(p_err))) + return + elif item['tag'] == 'Kaldır' or item['tag'] == 'Uninstall': + result_code, p_result, p_err = self.uninstall_package(item['packageName'], + item['version']) + + if result_code == 0: + self.logger.debug( + "Package installed : {0}={1}".format(item['packageName'], + item['version'])) + else: + self.logger.error( + "Package could not be installed : {0}={1}".format( + item['packageName'], + item['version'])) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='{0}\n Paket kaldırılırken ' + 'hata oluştu. Hata Mesajı: {1}'.format( + cn, str(p_err))) + except Exception as e: + self.logger.error('Unpredictable error exists. Error Message: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='{0}.\nÖngörülemeyen bir hata oluştu.Hata mesajı:{1}'.format( + cn, str(e))) + return + + self.logger.debug('Task handled successfully') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='{0} ahenginde, {1} paketi({2}) {3} işlemi başarı ile gerçekleştirildi.'.format(cn,item['packageName'], item['version'], item['tag'])) + except Exception as e: + self.logger.error('Unpredictable error exists. Error Message: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='{0}\nGörev çalıştırılırken beklenmedik bir hata oluştu. Hata Mesajı: {1}'.format( + cn, + str(e))) + + def is_repo_exist(self, source): + if source in open('/etc/apt/sources.list').read(): + return True + + for f_list_path in glob('/etc/apt/sources.list.d/*'): + if os.path.isfile(f_list_path): + if source in open(f_list_path).read(): + return True + return False + + def add_source(self, source): + self.write_file('/etc/apt/sources.list.d/ahenk.list', source+'\n', 'a+') + + +def handle_task(task, context): + plugin = Packages(task, context) + plugin.handle_task() diff --git a/src/plugins/package-manager/repositories.py b/src/plugins/package-manager/repositories.py new file mode 100644 index 0000000..a38b338 --- /dev/null +++ b/src/plugins/package-manager/repositories.py @@ -0,0 +1,50 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class PackageSourcesList(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + def handle_task(self): + error_message = "" + try: + result_code, p_out, p_err = self.execute( + '/bin/bash {}package-manager/scripts/sourcelist.sh'.format(self.Ahenk.plugins_path())) + data = {} + + if result_code != 0: + self.logger.error("Error occurred while listing repositories: " + str(p_err)) + error_message += " Paket depoları okunurken hata oluştu: " + str(p_err) + else: + data['packageSource'] = p_out + self.logger.debug("Repositories are listed") + + if not error_message: + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Paket depoları başarıyla okundu.', + data=json.dumps(data), content_type=self.get_content_type().APPLICATION_JSON.value) + else: + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message=error_message, + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.debug(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message="Paket depoları okunurken hata oluştu: " + str(e), + content_type=self.get_content_type().APPLICATION_JSON.value) + + +def handle_task(task, context): + plugin = PackageSourcesList(task, context) + plugin.handle_task() diff --git a/src/plugins/package-manager/scripts/sourcelist.sh b/src/plugins/package-manager/scripts/sourcelist.sh new file mode 100755 index 0000000..28c4c9d --- /dev/null +++ b/src/plugins/package-manager/scripts/sourcelist.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +for APT in `find /etc/apt/ -name \*.list`; do + grep -Po "(?<=^deb\s).*?(?=#|$)" $APT | while read ENTRY ; do + HOST=`echo $ENTRY | cut -d/ -f3` + USER=`echo $ENTRY | cut -d/ -f4` + PPA=`echo $ENTRY | cut -d/ -f5` + echo deb ${ENTRY} + done +done diff --git a/src/plugins/package-manager/show_package_archive.py b/src/plugins/package-manager/show_package_archive.py new file mode 100644 index 0000000..6a7543e --- /dev/null +++ b/src/plugins/package-manager/show_package_archive.py @@ -0,0 +1,67 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class ShowPackageArchive(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + def handle_task(self): + try: + package_name = str((self.data)['packageName']) + self.logger.debug('Package Installation History query is executing...') + a, result, b = self.execute( + ' cat /var/log/dpkg*.log | grep {} | grep "install \|upgrade"'.format(package_name)) + data = {} + res = [] + message = "" + self.logger.debug('Package archive info is being parsed...') + + if result is not None: + result_lines = result.splitlines() + for line in result_lines: + result_array = line.split(' ') + obj = {"installationDate": '{0} {1}'.format(result_array[0], result_array[1]), + "version": result_array[5], "operation": result_array[2], + "packageName": (result_array[3].split(':'))[0]} + res.append(obj) + + if a == 0 and len(res) > 0: + data = {"Result": res} + message = 'Paket arşivi başarıyla getirildi' + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message=message, + data=json.dumps(data), + content_type=self.get_content_type().APPLICATION_JSON.value) + elif a != 0: + message = 'Paket bulunamadı' + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message=message, + data=json.dumps(data), + content_type=self.get_content_type().APPLICATION_JSON.value) + else: + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message=message, + data=json.dumps(data), + content_type=self.get_content_type().APPLICATION_JSON.value) + self.logger.debug('Getting Package Archive task is handled successfully') + except Exception as e: + self.logger.debug(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Paket arşivi getirilirken beklenmedik hata!', + content_type=self.get_content_type().APPLICATION_JSON.value) + + +def handle_task(task, context): + plugin = ShowPackageArchive(task, context) + plugin.handle_task() diff --git a/src/plugins/remote-access/main.py b/src/plugins/remote-access/main.py new file mode 100644 index 0000000..5051001 --- /dev/null +++ b/src/plugins/remote-access/main.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Tuncay ÇOLAK + + +def info(): + inf = dict() + inf['name'] = 'remote-access' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'Remote Access provides controlling remote Ahenk machine simultaneously. It uses VNC technology.' + inf['task'] = True + inf['user_oriented'] = False + inf['machine_oriented'] = False + inf['developer'] = 'tuncay.colak@tubitak.gov.tr' + + return inf diff --git a/src/plugins/remote-access/setup-vnc-server.py b/src/plugins/remote-access/setup-vnc-server.py new file mode 100644 index 0000000..c46abc5 --- /dev/null +++ b/src/plugins/remote-access/setup-vnc-server.py @@ -0,0 +1,147 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Tuncay ÇOLAK +import json +import time +from base64 import b64encode +from os import urandom + +from base.plugin.abstract_plugin import AbstractPlugin + + +class SetupVnc(AbstractPlugin): + """docstring for SetupVnc""" + + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.password = self.create_password(10) + self.port = self.get_port_number() + self.logger.debug('Parameters were initialized') + + + def handle_task(self): + self.logger.debug('Handling task') + try: + self.run_vnc_server() + self.logger.info('VNC Server running') + ip_addresses = str(self.Hardware.Network.ip_addresses()).replace('[', '').replace(']', '').replace("'", '') + + self.data['port'] = self.port + self.data['password'] = self.password + self.data['host'] = ip_addresses + self.logger.debug('Response data created') + + if self.data['permission'] == "yes": + message = "VNC başarılı bir şekilde yapılandırıldı!\n{0} ip'li bilgisayara uzak erişim sağlanacaktır.\nKullanıcısının izni için lütfen bekleyiniz...'".format(self.data['host']) + elif self.data['permission'] == "no": + message = "VNC başarılı bir şekilde yapılandırıldı!\n{0} ip'li bilgisayara kullanıcı izni gerektirmeksizin uzak erişim sağlanmıştır...'".format(self.data['host']) + else: + message = "VNC başarılı bir şekilde yapılandırıldı!\n{0} ip'li bilgisayara kullanıcı izni ve bildirim gerektirmeksizin uzak erişim sağlanmıştır...'".format(self.data['host']) + + self.context.create_response(code=self.get_message_code().TASK_PROCESSED.value, + message=message, + data=json.dumps(self.data), + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.error('A problem occurred while running VNC server. Error Message: {}'.format(str(e))) + self.context.create_response(code=self.get_message_code().TASK_ERROR.value, + message='VNC sunucusu çalışırken bir hata oluştu.') + + def run_vnc_server(self): + + users = self.Sessions.user_name() + self.logger.debug('[XMessage] users : ' + str(users)) + + for user in users: + user_display = self.Sessions.display(user) + + self.logger.debug('Is VNC server installed?') + + if self.is_installed('x11vnc') is False: + self.logger.debug('VNC server not found, it is installing') + self.install_with_apt_get('x11vnc') + + self.logger.debug('VNC server was installed') + self.logger.debug('Killing running VNC proceses') + self.execute("ps aux | grep x11vnc | grep 'port " + self.port + "' | awk '{print $2}' | xargs kill -9", + result=False) + self.logger.debug('Running VNC proceses were killed') + self.logger.debug('Getting display and username...') + + arr = self.get_username_display() + + if len(arr) < 1: + raise NameError('Display not found!') + + params = str(arr[0]).split(' ') + + self.logger.info("--------->>>> "+str(params)) + + self.logger.debug('Username:{0} Display:{1}'.format(params[0], params[1])) + + if self.is_exist('/home/{0}/.vncahenk{0}'.format(params[0])) is True: + self.logger.debug('Cleaning previous configurations.') + # self.delete_folder('/home/{0}/.vncahenk{0}'.format(params[0])) + + self.logger.debug('Creating user VNC conf file as user') + self.execute('su - {0} -c "mkdir -p /home/{0}/.vncahenk{1}"'.format(params[0], params[0]), result=False) + + self.logger.debug('Creating password as user') + self.execute('su - {0} -c "x11vnc -storepasswd {1} /home/{0}/.vncahenk{2}/x11vncpasswd"'.format(params[0], self.password, params[0]), result=False) + + self.logger.debug('Running VNC server as user.') + + if self.data['permission'] == "yes": + self.send_notify("Liderahenk", "Lider Ahenk Sistem Yoneticisi tarafindan\n5 sn sonra bilgisayarınıza uzak erişim sağlanacaktır.\nBağlantı kapatıldıktan sonra ayrıca bilgilendirilecektir.",":0", params[0], timeout=50000) + time.sleep(5) + + self.execute('su - {0} -c "x11vnc -accept \'popup\' -gone \'popup\' -rfbport {1} -rfbauth /home/{0}/.vncahenk{2}/x11vncpasswd -o /home/{0}/.vncahenk{3}/vnc.log -display :{4}"'.format( + params[0], self.port, params[0], params[0], params[1]), result=False) + elif self.data["permission"] == "no": + + self.logger.info("Lider Ahenk sistem yöneticisi 5 sn sonra bilgisayarınıza uzak erişim sağlayacaktır. ") + + self.send_notify("Liderahenk", "Lider Ahenk Sistem Yoneticisi tarafindan\n5 sn sonra bilgisayarınıza uzak erişim sağlanacaktır.\nBağlantı kapatıldıktan sonra ayrıca bilgilendirilecektir.", ":0", params[0], timeout=50000) + time.sleep(5) + + self.execute('su - {0} -c "x11vnc -gone \'popup\' -rfbport {1} -rfbauth /home/{0}/.vncahenk{2}/x11vncpasswd -o /home/{0}/.vncahenk{3}/vnc.log -display :{4}"'.format( + params[0], self.port, params[0], params[0], params[1]), result=False) + + else: + self.execute( + 'su - {0} -c "x11vnc -rfbport {1} -rfbauth /home/{0}/.vncahenk{2}/x11vncpasswd -o /home/{0}/.vncahenk{3}/vnc.log -display :{4}"'.format( + params[0], self.port, params[0], params[0], params[1]), result=False) + self.logger.info("Lider Ahenk sistem yöneticisi tarafından kullanıcı izni ve bildirim gerektirmeksizin uzak erişim sağlanmıştır") + + def get_username_display(self): + result_code, p_out, p_err = self.execute("who | awk '{print $1, $5}' | sed 's/(://' | sed 's/)//'", result=True) + + self.logger.debug('Getting display result code:{0}'.format(str(result_code))) + + result = [] + lines = str(p_out).split('\n') + for line in lines: + arr = line.split(' ') + if len(arr) > 1 and str(arr[1]).isnumeric() is True: + result.append(line) + return result + + def create_password(self, pass_range): + self.logger.debug('Password created') + random_bytes = urandom(pass_range) + return b64encode(random_bytes).decode('utf-8') + + def get_port_number(self): + self.logger.debug('Target port is 5999') + return '5999' + + + +def handle_task(task, context): + vnc = SetupVnc(task, context) + vnc.handle_task() + diff --git a/src/plugins/resource-usage/main.py b/src/plugins/resource-usage/main.py new file mode 100644 index 0000000..41743b8 --- /dev/null +++ b/src/plugins/resource-usage/main.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +def info(): + inf = dict() + inf['name'] = 'resource-usage' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'Resource Usage Info and Resource Usage Controls and Warnings are handled with this plugin' + inf['task'] = True + inf['user_oriented'] = False + inf['machine_oriented'] = False + inf['developer'] = 'cemre.alpsoy@agem.com.tr' + + return inf diff --git a/src/plugins/resource-usage/resource_info_alert.py b/src/plugins/resource-usage/resource_info_alert.py new file mode 100644 index 0000000..3a7f613 --- /dev/null +++ b/src/plugins/resource-usage/resource_info_alert.py @@ -0,0 +1,79 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY +# Author: Emre Akkaya + +from base.plugin.abstract_plugin import AbstractPlugin +from threading import Thread +import time +import psutil +import json + + +class ResourceUsage(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + self.is_running = False + self._threadCep = None + + def handle_task(self): + try: + action = self.data['action'] + self.logger.debug("Action: {0}".format(action)) + + if action == "start_timer": + self.is_running = True + with open('is_running.txt', 'w') as f: + f.write("%s" % str('true')) + self._threadCep = Thread(target=self.run_timer, + args=(int(self.data['interval']), self.context.get('task_id'))) + self._threadCep.start() + data = {"status": "started"} + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Kaynak kullanım bilgileri toplanmaya başlandı.', data=json.dumps(data), content_type=self.get_content_type().APPLICATION_JSON.value) + elif action == "stop_timer": + self.is_running = False + with open('is_running.txt', 'w') as f: + f.write("%s" % str('false')) + data = {"action": "stop"} + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Kaynak kullanım bilgilerinin toplanması durduruldu.', + data=json.dumps(data), content_type=self.get_content_type().APPLICATION_JSON.value) + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Kaynak kullanım bilgileri işlenirken hata oluştu: ' + str(e)) + + def run_timer(self, interval, task_id): + while self.is_running: + self.gather_resource_usage(task_id) + time.sleep(interval) + with open('is_running.txt', 'r') as f: + if f.read() == 'false': + self.is_running = False + + def gather_resource_usage(self, task_id): + # Memory usage + memory_usage = psutil.virtual_memory() + self.logger.debug("Memory usage: {0}".format(memory_usage)) + # Disk usage + disk_usage = psutil.disk_usage('/') + self.logger.debug("Disk usage: {0}".format(disk_usage)) + # CPU usage + cpu_percentage = psutil.cpu_percent(interval=1) + self.logger.debug("CPU percentage: {0}".format(cpu_percentage)) + + data = {'memoryUsage': str(memory_usage), 'diskUsage': str(disk_usage), 'cpuPercentage': str(cpu_percentage)} + command = 'python3 /opt/ahenk/ahenkd.py send -t {0} -m {1} -s'.format(task_id, json.dumps(str(data))) + result_code, p_out, p_err = self.execute(command) + if result_code != 0: + self.logger.error("Error occurred while sending message: " + str(p_err)) + + +def handle_task(task, context): + plugin = ResourceUsage(task, context) + plugin.handle_task() diff --git a/src/plugins/resource-usage/resource_info_fetcher.py b/src/plugins/resource-usage/resource_info_fetcher.py new file mode 100644 index 0000000..d905f04 --- /dev/null +++ b/src/plugins/resource-usage/resource_info_fetcher.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY +# Author: Emre Akkaya + +from base.plugin.abstract_plugin import AbstractPlugin +import json + + +class ResourceUsage(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + def handle_task(self): + try: + device = "" + self.logger.debug("Gathering resource usage for disk, memory and CPU.") + for part in self.Hardware.Disk.partitions(): + if len(device) != 0: + device += ", " + device = device + part.device + data = {'System': self.Os.name(), 'Release': self.Os.kernel_release(), + 'Version': self.Os.distribution_version(), 'Machine': self.Os.architecture(), + 'CPU Physical Core Count': self.Hardware.Cpu.physical_core_count(), + 'Total Memory': self.Hardware.Memory.total(), + 'Usage': self.Hardware.Memory.used(), + 'Total Disc': self.Hardware.Disk.total(), + 'Usage Disc': self.Hardware.Disk.used(), + 'Processor': self.Hardware.Cpu.brand(), + 'Device': device, + 'CPU Logical Core Count': self.Hardware.Cpu.logical_core_count(), + 'CPU Actual Hz': self.Hardware.Cpu.hz_actual(), + 'CPU Advertised Hz': self.Hardware.Cpu.hz_advertised() + } + self.logger.debug("Resource usage info gathered.") + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Anlık kaynak kullanım bilgisi başarıyla toplandı.', + data=json.dumps(data), content_type=self.get_content_type().APPLICATION_JSON.value) + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Anlık kaynak kullanım bilgisi toplanırken hata oluştu: {0}'.format(str(e))) + + +def handle_task(task, context): + plugin = ResourceUsage(task, context) + plugin.handle_task() diff --git a/src/plugins/resource-usage/send_mail.py b/src/plugins/resource-usage/send_mail.py new file mode 100644 index 0000000..c381f72 --- /dev/null +++ b/src/plugins/resource-usage/send_mail.py @@ -0,0 +1,31 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +from base.plugin.abstract_plugin import AbstractPlugin +import json + + +class SendMail(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + def handle_task(self): + try: + self.logger.debug("[RESOURCE USAGE] Send mail task is started.") + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='E posta gönderim bilgileri başarıyla iletildi', + data=json.dumps(self.data), content_type=self.get_content_type().APPLICATION_JSON.value) + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='e posta gönderim bilgileri edinilirken hata oluştu: {0}'.format(str(e))) + + +def handle_task(task, context): + plugin = SendMail(task, context) + plugin.handle_task() diff --git a/src/plugins/resource-usage/shutdown.py b/src/plugins/resource-usage/shutdown.py new file mode 100644 index 0000000..d7a9505 --- /dev/null +++ b/src/plugins/resource-usage/shutdown.py @@ -0,0 +1,45 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin +from base.system.system import System +import json + + +class ShutDownMachine(AbstractPlugin): + def __init__(self, task, context): + super(AbstractPlugin, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.shut_down_command = 'sleep 5s; shutdown -h now' + + self.logger.debug('[RESOURCE USAGE - Shut Down Machine] Parameters were initialized.') + + def handle_task(self): + try: + self.logger.debug('[RESOURCE USAGE - Shut Down Machine] Shutting down the machine...') + self.execute(self.shut_down_command, result=False) + + response = 'Shutdown komutu başarıyla çalıştırıldı. Bilgisayar kapatılacak. Mac Adres(ler)i: {0}, Ip Adres(ler)i: {1}' \ + .format(System.Hardware.Network.mac_addresses(), System.Hardware.Network.ip_addresses()) + data = {"shutdown": "true"} + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + data=json.dumps(data), message=response, + content_type=self.get_content_type().APPLICATION_JSON.value) + self.logger.info('[SEOURCE USAGE - Shut Down Machine] task is handled successfully') + + except Exception as e: + self.logger.error( + '[RESOURCE USAGE - Shut Down Machine] A problem occured while handling Shutdown task: {0}'.format( + str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Makina kapatılırken bir hata oluştu: {0}'.format(str(e))) + + +def handle_task(task, context): + shut_down = ShutDownMachine(task, context) + shut_down.handle_task() diff --git a/src/plugins/rsyslog/main.py b/src/plugins/rsyslog/main.py new file mode 100644 index 0000000..23c42e1 --- /dev/null +++ b/src/plugins/rsyslog/main.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +def info(): + inf = dict() + inf['name'] = 'rsyslog' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'System Log file configuration and rotation info are defined with this plugin.' + inf['task'] = False + inf['user_oriented'] = False + inf['machine_oriented'] = True + inf['developer'] = 'cemre.alpsoy@agem.com.tr' + + return inf diff --git a/src/plugins/rsyslog/policy.py b/src/plugins/rsyslog/policy.py new file mode 100644 index 0000000..2c392fa --- /dev/null +++ b/src/plugins/rsyslog/policy.py @@ -0,0 +1,116 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + + +# Author: Cemre ALPSOY + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Rsyslog(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + self.rsyslog_path = '/etc/rsyslog.d/ahenk.conf' + self.default_config = "# /etc/rsyslog.conf\tConfiguration file for rsyslog.\n#\n#\t\t\tFor more information see\n#\t\t\t/usr/share/doc/rsyslog-doc/html/rsyslog_conf.html\n#\n# Default logging rules can be found in /etc/rsyslog.d/50-default.conf\n\n\n#################\n#### MODULES ####\n#################\n\n$ModLoad imuxsock # provides support for local system logging\n$ModLoad imklog # provides kernel logging support\n#$ModLoad immark # provides --MARK-- message capability\n\n# provides UDP syslog reception\n#$ModLoad imudp\n#$UDPServerRun 514\n\n# provides TCP syslog reception\n$ModLoad imtcp\n$InputTCPServerRun 514\n\n# Enable non-kernel facility klog messages\n$KLogPermitNonKernelFacility on\n\n###########################\n#### GLOBAL DIRECTIVES ####\n###########################\n\n#\n# Use traditional timestamp format.\n# To enable high precision timestamps, comment out the following line.\n#\n$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat\n\n# Filter duplicated messages\n$RepeatedMsgReduction on\n\n#\n# Set the default permissions for all log files.\n#\n$FileOwner syslog\n$FileGroup adm\n$FileCreateMode 0640\n$DirCreateMode 0755\n$Umask 0022\n$PrivDropToUser syslog\n$PrivDropToGroup syslog\n\n#\n# Where to place spool and state files\n#\n$WorkDirectory /var/spool/rsyslog\n\n#\n# Include all config files in /etc/rsyslog.d/\n#\n$IncludeConfig /etc/rsyslog.d/*.conf\n\n#RULE_STR#\n\n#\n# Logging for the mail system. Split it up so that\n# it is easy to write scripts to parse these files.\n#\n#mail.info\t\t\t-/var/log/mail.info\n#mail.warn\t\t\t-/var/log/mail.warn\nmail.err\t\t\t/var/log/mail.err\n\n#\n# Logging for INN news system.\n#\nnews.crit\t\t\t/var/log/news/news.crit\nnews.err\t\t\t/var/log/news/news.err\nnews.notice\t\t\t-/var/log/news/news.notice\n\n#\n# Some \"catch-all\" log files.\n#\n#*.=debug;\\\n#\tauth,authpriv.none;\\\n#\tnews.none;mail.none\t-/var/log/debug\n#*.=info;*.=notice;*.=warn;\\\n#\tauth,authpriv.none;\\\n#\tcron,daemon.none;\\\n#\tmail,news.none\t\t-/var/log/messages\n\n#\n# Emergencies are sent to everybody logged in.\n#\n*.emerg :omusrmsg:*\n\n#\n# I like to have messages displayed on the console, but only on a virtual\n# console I usually leave idle.\n#\n#daemon,mail.*;\\\n#\tnews.=crit;news.=err;news.=notice;\\\n#\t*.=debug;*.=info;\\\n#\t*.=notice;*.=warn\t/dev/tty8\n\n# The named pipe /dev/xconsole is for the `xconsole\' utility. To use it,\n# you must invoke `xconsole\' with the `-file\' option:\n# \n# $ xconsole -file /dev/xconsole [...]\n#\n# NOTE: adjust the list below, or you\'ll go crazy if you have a reasonably\n# busy site..\n#\ndaemon.*;mail.*;\\\n\tnews.err;\\\n\t*.=debug;*.=info;\\\n\t*.=notice;*.=warn\t|/dev/xconsole" + self.rsyslog_conf = "" + self.remote_conf = "" + self.protocol = '@@' + + self.rsyslog_conf_file_path = '/etc/rsyslog.conf' + self.remote_conf_file_path = '/etc/rsyslog.d/remote.conf' + self.log_rotate_conf_file_path = '/etc/logrotate.conf' + + def handle_policy(self): + try: + if str(json.loads(self.data)['PROTOCOL']) == 'UDP': + self.protocol = '@' + self.logger.debug('Handling profile ...') + items = json.loads(self.data)['items'] + for item in items: + if str(item['isLocal']).upper() == 'EVET' or str(item['isLocal']).upper() == 'YES': + self.rsyslog_conf += str(item['recordDescription']) + '\t' + str(item['logFilePath']) + '\n' + else: + self.remote_conf += str(item['recordDescription']) + ' ' + self.protocol + str( + json.loads(self.data)['ADDRESS']) + ':' + str(json.loads(self.data)['PORT']) + '\n' + self.rsyslog_conf = self.default_config.replace("#RULE_STR#", self.rsyslog_conf) + self.logger.debug('Rsyslog config files are ready') + (result_code, p_out, p_err) = self.execute( + "find /etc/rsyslog.d/ -name '*.conf' -exec bash -c 'sudo mv ${0/conf/conf.orig}' {} \;", shell=True) + if str(result_code) == '0': + self.logger.debug('Backup up old config files.') + else: + self.logger.debug('Error while backing up old config files') + + rsyslog_conf_contents = str(self.rsyslog_conf).strip() + self.logger.debug(self.rsyslog_conf_file_path + ': \n' + rsyslog_conf_contents + '\n') + + config_file = open(self.rsyslog_conf_file_path, 'w+') + config_file.write(rsyslog_conf_contents) + config_file.close() + remote_conf_contents = str(self.remote_conf).strip() + # self.logger.debug(self.remote_conf_file_path + ': \n' + remote_conf_contents + '\n') + if remote_conf_contents and not remote_conf_contents.isspace(): + self.logger.debug('Updating remote.conf') + remote_config_file = open(self.remote_conf_file_path, 'w+') + remote_config_file.write(remote_conf_contents) + remote_config_file.close() + else: + self.logger.debug('CANNOT update remote.conf') + self.execute('service rsyslog restart', shell=True) + self.logger.debug('Rsyslog service restarted.') + self.logger.debug('Processing logrotate config') + rotation_interval = str(json.loads(self.data)['rotationInterval']) + keep_back_logs = str(json.loads(self.data)['keepBacklogs']) + max_size = str(json.loads(self.data)['maxSize']) + create_new_log_files = json.loads(self.data)['createNewLogFiles'] + compress_old_log_files = json.loads(self.data)['compressOldLogFiles'] + missing_ok = json.loads(self.data)['missingOk'] + f = open(self.log_rotate_conf_file_path, 'w') + if rotation_interval: + f.write(rotation_interval + '\n') + else: + f.write('weekly\n') + if keep_back_logs: + f.write('rotate ' + keep_back_logs + '\n') + else: + f.write('rotate 4\n') + if max_size: + f.write('maxsize ' + max_size + 'M\n') + if create_new_log_files: + f.write('create\n') + if compress_old_log_files: + f.write('compress\n') + if missing_ok: + f.write('missingok\n') + f.write('include /etc/logrotate.d\n') + f.close() + self.logger.debug('Rsyslog Profile Processed') + self.context.create_response(code=self.message_code.POLICY_PROCESSED.value, + message='Ajan Rsyslog Profili başarıyla uygulandı.', + content_type=self.get_content_type().APPLICATION_JSON.value) + except Exception as e: + self.logger.error( + 'A problem occurred while applying rsyslog profile. Error Message: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.POLICY_ERROR.value, + message='Rsyslog Profili uygulanırken bir hata oluştu.', + content_type=self.get_content_type().APPLICATION_JSON.value) + + +def handle_policy(profile_data, context): + plugin = Rsyslog(profile_data, context) + plugin.handle_policy() + + +class Item(object): + record_description = "" + log_file_path = "" + + def __init__(self, record_description, log_file_path): + self.record_description = record_description + self.log_file_path = log_file_path diff --git a/src/plugins/script/execute_script.py b/src/plugins/script/execute_script.py new file mode 100644 index 0000000..926ec18 --- /dev/null +++ b/src/plugins/script/execute_script.py @@ -0,0 +1,94 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Emre Akkaya + +from base.plugin.abstract_plugin import AbstractPlugin +import json + + +class ExecuteScript(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.temp_file_name = str(self.generate_uuid()) + self.base_file_path = '{0}{1}'.format(str(self.Ahenk.received_dir_path()), self.temp_file_name) + + def handle_task(self): + try: + script_type = self.data['SCRIPT_TYPE'] + script_contents = self.format_contents(self.data['SCRIPT_CONTENTS']) + script_params = self.data['SCRIPT_PARAMS'] + file_path = self.base_file_path + + # Determine script extension and command + command = '' + if script_type == 'BASH': + file_path += '.sh' + command += 'bash' + elif script_type == 'PYTHON': + file_path += '.py' + command += 'python' + elif script_type == 'PERL': + file_path += '.pl' + command += 'perl' + elif script_type == 'RUBY': + file_path += '.rb' + command += 'ruby' + + self.create_script_file(file_path, script_contents) + + result_code, p_out, p_err = self.execute_script_file(command, file_path, script_params) + if result_code != 0: + self.logger.error("Error occurred while executing script: " + str(p_err)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Betik çalıştırılırken hata oluştu', + data=json.dumps({'Result': p_err}), + content_type=self.get_content_type().APPLICATION_JSON.value) + else: + self.logger.debug("Executed script file.") + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Betik başarıyla çalıştırıldı.', + data=json.dumps({'Result': p_out}), + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Betik çalıştırılırken hata oluştu:' + str(e), + content_type=self.get_content_type().APPLICATION_JSON.value) + + def create_script_file(self, file_path, script_contents): + self.logger.debug("Creating script file.") + # Create temporary script file with the provided content + self.write_file(file_path, script_contents) + + # Make the file executable + self.make_executable(file_path) + self.logger.debug("Created script file: {0}".format(file_path)) + + def execute_script_file(self, command, file_path, script_params): + self.logger.debug("Executing script file.") + # Execute the file + if not script_params: + return self.execute('{0} {1}'.format(command, file_path)) + else: + return self.execute('{0} {1} {2}'.format(command, file_path, script_params)) + + @staticmethod + def format_contents(contents): + tmp = contents + replacements = list() + replacements.append(('&dbq;', '\"')) + replacements.append(('&sgq;', '\'')) + for r, n in replacements: + tmp = tmp.replace(r, n) + return tmp + + +def handle_task(task, context): + plugin = ExecuteScript(task, context) + plugin.handle_task() diff --git a/src/plugins/script/main.py b/src/plugins/script/main.py new file mode 100644 index 0000000..fb986d0 --- /dev/null +++ b/src/plugins/script/main.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +def info(): + inf = dict() + inf['name'] = 'script' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = '' + inf['task'] = True + inf['user_oriented'] = False + inf['machine_oriented'] = False + inf['developer'] = '' + + return inf diff --git a/src/plugins/script/policy.py b/src/plugins/script/policy.py new file mode 100644 index 0000000..7a83d8b --- /dev/null +++ b/src/plugins/script/policy.py @@ -0,0 +1,102 @@ +# !/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Script(AbstractPlugin): + def __init__(self, data, context): + super(Script, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.machine_profile = True + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.temp_file_name = str(self.generate_uuid()) + self.base_file_path = '{0}{1}'.format(str(self.Ahenk.received_dir_path()), self.temp_file_name) + + def handle_policy(self): + try: + json_data = json.loads(self.data) + + script_type = str(json_data['SCRIPT_TYPE']) + script_contents = self.format_contents(str(json_data['SCRIPT_CONTENTS'])) + script_params = str(json_data['SCRIPT_PARAMS']) + file_path = self.base_file_path + + # Determine script extension and command + command = '' + if script_type == 'BASH': + file_path += '.sh' + command += 'bash' + elif script_type == 'PYTHON': + file_path += '.py' + command += 'python' + elif script_type == 'PERL': + file_path += '.pl' + command += 'perl' + elif script_type == 'RUBY': + file_path += '.rb' + command += 'ruby' + + self.create_script_file(file_path, script_contents) + + result_code, p_out, p_err = self.execute_script_file(command, file_path, script_params) + if result_code != 0: + self.logger.error("Error occurred while executing script: " + str(p_err)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Betik çalıştırılırken hata oluştu', + data=json.dumps({'Result': p_err}), + content_type=self.get_content_type().APPLICATION_JSON.value) + else: + self.logger.debug("Executed script file.") + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Betik başarıyla çalıştırıldı.', + data=json.dumps({'Result': p_out}), + content_type=self.get_content_type().APPLICATION_JSON.value) + + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Betik çalıştırılırken hata oluştu:' + str(e), + content_type=self.get_content_type().APPLICATION_JSON.value) + + def create_script_file(self, file_path, script_contents): + self.logger.debug("Creating script file.") + # Create temporary script file with the provided content + self.write_file(file_path, script_contents) + + # Make the file executable + self.make_executable(file_path) + self.logger.debug("Created script file: {0}".format(file_path)) + + def execute_script_file(self, command, file_path, script_params): + self.logger.debug("Executing script file.") + # Execute the file + if not script_params: + return self.execute('{0} {1}'.format(command, file_path)) + else: + return self.execute('{0} {1} {2}'.format(command, file_path, script_params)) + + @staticmethod + def format_contents(contents): + tmp = contents + replacements = list() + replacements.append(('&dbq;', '\"')) + replacements.append(('&sgq;', '\'')) + for r, n in replacements: + tmp = tmp.replace(r, n) + return tmp + + + +def handle_policy(profile_data, context): + plugin = Script(profile_data, context) + plugin.handle_policy() diff --git a/src/plugins/service/get_services.py b/src/plugins/service/get_services.py new file mode 100644 index 0000000..3b15730 --- /dev/null +++ b/src/plugins/service/get_services.py @@ -0,0 +1,128 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +import json +import subprocess +from base.plugin.abstract_plugin import AbstractPlugin + + +class ServiceList(object): + def __init__(self): + self.service_list = [] + + +class ServiceListItem: + def __init__(self, service_name, status, auto): + self.serviceName = service_name + self.serviceStatus = status + self.startAuto = auto + + +def encode_service_object(obj): + if isinstance(obj, ServiceListItem): + return obj.__dict__ + return obj + + +class GetServices(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.temp_file_name = str(self.generate_uuid()) + self.file_path = '{0}{1}'.format(str(self.Ahenk.received_dir_path()), self.temp_file_name) + self.service_status = 'systemctl status {}' + self.isRecordExist = 0 + + def handle_task(self): + try: + self.logger.debug('Executing command for service list.') + self.get_service_status() + + self.logger.debug('Command executed.') + + if self.is_exist(self.file_path): + data = {} + self.logger.debug(str(self.file_path)) + md5sum = self.get_md5_file(str(self.file_path)) + self.logger.debug('{0} renaming to {1}'.format(self.temp_file_name, md5sum)) + self.rename_file(self.file_path, self.Ahenk.received_dir_path() + '/' + md5sum) + self.logger.debug('Renamed.' + self.Ahenk.received_dir_path() + '/' + md5sum) + data['md5'] = md5sum + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Servis listesi başarıyla okundu.', + data=json.dumps(data), + content_type=self.get_content_type().TEXT_PLAIN.value) + self.logger.debug("Execution Info fetched succesfully. ") + self.logger.debug("Execution Info has sent") + else: + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Servis listesi getirilemedi') + self.logger.debug('Service list created successfully') + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Servis listesi oluşturulurken hata oluştu: ' + str(e)) + + def add_file(self, name, status, auto_start): + if self.isRecordExist == 0: + self.execute('echo { \\"agent\\" : \\"'+ self.Ahenk.dn() + '\\", \\"service_list\\" :[ >> ' + self.file_path) + self.isRecordExist = 1 + t_command = 'echo "{ \\"serviceName\\": \\"' + name + '\\", \\"serviceStatus\\": \\"' + status + '\\", \\"startAuto\\":\\"' + auto_start + '\\"}" >> ' + self.file_path + self.execute(t_command) + self.execute('echo , >> ' + self.file_path) + + def add_agentDnToFile(self): + t_command = 'echo "{ \\"agent\\": \\"' + self.Ahenk.dn() +'\\"}" >> ' + self.file_path + self.execute(t_command) + self.execute('echo , >> ' + self.file_path) + + def get_service_status(self): + try: + (result_code, p_out, p_err) = self.execute("systemctl list-units --type service --all | grep loaded") + self.create_file(self.file_path) + # service_list = ServiceList() + lines = p_out.split('\n') + for line in lines: + line_split = line.split(' ') + service=[] + for word in line_split: + if word != '' : + service.append(word) + if len(service)>0 and '.service' not in service[0]: + del service[0] + + if len(service)>0 and '.service' in service[0]: # service[0] = service name, service[1] is loaded, service[2] active or not, + result, out, err = self.execute(self.service_status.format(service[0])) # check service is enable or not on auto start + auto='INACTIVE' + if 'disabled' in out: + auto='INACTIVE' + elif 'enabled' in out: + auto='ACTIVE' + + if service[2] == 'active': + self.add_file(service[0], "ACTIVE", auto) + else: + self.add_file(service[0], 'INACTIVE',auto) + + print(service) + + + if self.isRecordExist == 1: + self.execute("sed -i '$ d' " + self.file_path) + self.execute('echo "]}" >> ' + self.file_path) + + except Exception as e: + print(str(e)) + self.logger.error(str(e)) + + + + +def handle_task(task, context): + plugin = GetServices(task, context) + plugin.handle_task() diff --git a/src/plugins/service/init.py b/src/plugins/service/init.py new file mode 100644 index 0000000..2834b1f --- /dev/null +++ b/src/plugins/service/init.py @@ -0,0 +1,21 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Init(AbstractPlugin): + def __init__(self, context): + super(Init, self).__init__() + self.context = context + self.logger = self.get_logger() + + def handle_mode(self): + if self.is_installed('chkconfig') is False: + self.install_with_apt_get('chkconfig') + pass + + +def handle_mode(context): + init = Init(context) + init.handle_mode() diff --git a/src/plugins/service/main.py b/src/plugins/service/main.py new file mode 100644 index 0000000..fef57eb --- /dev/null +++ b/src/plugins/service/main.py @@ -0,0 +1,15 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +def info(): + inf = dict() + inf['name'] = 'service' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'Agent machine all service settings (service start/stop operations, run a service/services as a start-up service...etc.) are defined with this plugin.' + inf['task'] = True + inf['user_oriented'] = False + inf['machine_oriented'] = False + inf['developer'] = 'cemre.alpsoy@agem.com.tr' + + return inf diff --git a/src/plugins/service/service_list.py b/src/plugins/service/service_list.py new file mode 100644 index 0000000..b93bda9 --- /dev/null +++ b/src/plugins/service/service_list.py @@ -0,0 +1,101 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class ServiceList(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + def start_stop_service(self, item, service_action): + + service_name=str(item['serviceName']) + (result_code, p_out, p_err) = self.execute('systemctl {0} {1}'.format(service_action,service_name)) + if result_code == 0: + message = 'Service:{0} , Status:{1} '.format(service_name,service_action) + else: + message = '{0} service action was unsuccessful: {0}, return code {1}'.format(service_name, service_action, str(result_code)) + + self.logger.debug(message) + return result_code, message, item + + def set_startup_service(self, service_name, action): + (result_code, p_out, p_err) = self.execute('update-rc.d {0} {1}'.format(service_name, action)) + if result_code == 0: + message = 'Service startup action was successful: {}'.format(service_name) + else: + message = 'Service action was unsuccessful: {0}, return code {1}'.format(service_name, str(result_code)) + + self.logger.debug(message) + return result_code, message + + def get_service_status(self, service_item): + + service_name=str(service_item['serviceName']) + result, p_out, err = self.execute('systemctl status {0}'.format(service_name)) + if 'not-found' in p_out: + service_item["serviceStatus"] = 'Service Not Found' + + elif 'running' in p_out: + service_item["serviceStatus"] = 'Running' + + elif ('inactive' in p_out) or ('failed' in p_out): + service_item["serviceStatus"] = 'Stopped' + + return service_item + + + def handle_task(self): + self.logger.debug('Handling Service Task') + try: + items = (self.data)['serviceRequestParameters'] + resultMessage = "" + for item in items: + try: + if item['serviceStatus'] is not None and ( + str(item['serviceStatus']) == 'Başlat' or str(item['serviceStatus']) == 'Start' or str(item['serviceStatus']) == 'START' ): + resultcode, message, item = self.start_stop_service(item, "start") + resultMessage += message + if item['serviceStatus'] is not None and ( + str(item['serviceStatus']) == 'Durdur' or str(item['serviceStatus']) == 'Stop' or str(item['serviceStatus']) == 'STOP' ): + + resultcode, message, item= self.start_stop_service(item, "stop") + resultMessage += message + if item['startAuto'] is not None and ( + str(item['startAuto']) == 'Başlat' or str(item['startAuto']) == 'Start' or str(item['startAuto']) == 'START'): + resultcode, message = self.set_startup_service(item, "defaults") + resultMessage += message + if item['startAuto'] is not None and ( + str(item['startAuto']) == 'Durdur' or str(item['startAuto']) == 'Stop' or str(item['startAuto']) == 'STOP' ): + resultcode, message = self.set_startup_service(item, "remove") + resultMessage += message + + item=self.get_service_status(item) + + except Exception as e: + resultMessage += '{0} servisinin isteklerini gerçekleştirirken hata ile karşılaşıldı. Hdata : {1}\r\n'.format( + str(item['serviceName']), str(e)) + self.logger.debug(resultMessage) + data = {'ResultMessage': resultMessage, 'service_list': items } + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Servis istekleri gerçekleştirildi', + data=json.dumps(data), + content_type=self.get_content_type().APPLICATION_JSON.value) + except Exception as e: + self.logger.debug('Service List Exception :' + str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Servis istekleri gerçekleştirilirken beklenmedik hata!') + + +def handle_task(task, context): + plugin = ServiceList(task, context) + plugin.handle_task() diff --git a/src/plugins/service/service_management.py b/src/plugins/service/service_management.py new file mode 100644 index 0000000..1e81ca6 --- /dev/null +++ b/src/plugins/service/service_management.py @@ -0,0 +1,227 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Author: Cemre ALPSOY + +from base.plugin.abstract_plugin import AbstractPlugin +import json + + +class ServiceManagement(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + self.service_status = 'service {} status' + + def start_stop_service(self, service_name, service_action): + (result_code, p_out, p_err) = self.execute('service {0} {1}'.format(service_name, service_action)) + if result_code == 0: + message = 'Service start/stop action was successful: '.format(service_action) + + else: + message = 'Service action was unsuccessful: {0}, return code {1}'.format(service_action, str(result_code)) + + self.logger.debug(message) + return result_code, message + + def is_service_exist(self, service_name): + result_code, p_out, p_err = self.execute("service --status-all") + p_err = ' ' + p_err + p_out = ' ' + p_out + lines = p_out.split('\n') + for line in lines: + line_split = line.split(' ') + if len(line_split) >= 5: + result, out, err = self.execute(self.service_status.format(service_name)) + + if 'Unknown job' not in str(err): + if line_split[len(line_split) - 4] == '+': + return "ACTIVE" + elif line_split[len(line_split) - 4] == '-': + return "INACTIVE" + else: + return "NOTFOUND" + + def is_service_running(self, service_name): + result_code, p_out, p_err = self.execute("ps -A") + if service_name in p_out: + return True + else: + return False + + def set_startup_service(self, service_name): + (result_code, p_out, p_err) = self.execute('update-rc.d {} defaults'.format(service_name)) + + if result_code == 0: + message = 'Service startup action was successful: {}'.format(service_name) + else: + message = 'Service action was unsuccessful: {0}, return code {1}'.format(service_name, str(result_code)) + + self.logger.debug('SERVICE' + message) + return result_code, message + + def send_mail(self, stopped_services, all_services): + if self.context.is_mail_send(): + mail_content = self.context.get_mail_content(); + if mail_content.__contains__('{stopped_services}'): + mail_content = str(mail_content).replace('{stopped_services}', str(stopped_services)); + if mail_content.__contains__('{ahenk}'): + mail_content = str(mail_content).replace('{ahenk}', str(self.Ahenk.dn())); + + self.context.set_mail_content(mail_content) + + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Servis izleme görevi başarıyla oluşturuldu.', + data=json.dumps({ + 'Result': 'İşlem Başarı ile gercekleştirildi', + 'mail_content': str(self.context.get_mail_content()), + 'mail_subject': str(self.context.get_mail_subject()), + 'mail_send': self.context.is_mail_send(), + 'services': all_services + }), + content_type=self.get_content_type().APPLICATION_JSON.value) + else: + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Servis izleme görevi başarıyla oluşturuldu.', + data=json.dumps({ + 'Result': 'İşlem Başarı ile gercekleştirildi', + 'mail_send': self.context.is_mail_send(), + 'services': all_services + }), + content_type=self.get_content_type().APPLICATION_JSON.value) + + def save_service(self, service): + cols = ['serviceName', 'serviceStatus', 'timestamp', 'task_id']; + values = [service["serviceName"], service["serviceStatus"], self.timestamp(),service['task_id']] + return self.db_service.update('service', cols, values) + + def save_service_list(self, service_list): + cols = ['serviceName', 'serviceStatus', 'timestamp', 'task_id']; + values = [service_list[1], service_list[2], self.timestamp(), service_list[4]] + return self.db_service.update('service', cols, values, 'id='+ str(service_list[0])) + + def get_service_status(self,service): + service_name = service["serviceName"] + ".service" + serviceStatus = service["serviceStatus"] + result_code, p_out, p_err = self.execute("systemctl status " + str(service_name) + " | grep 'Active\|Loaded'") + # self.logger.debug("-----p_out"+ str(p_out)) + if 'not-found' in p_out: + service["serviceStatus"] = 'Service Not Found' + + elif 'running' in p_out: + service["serviceStatus"] = 'Running' + + elif ('inactive' in p_out) or ('failed' in p_out): + service["serviceStatus"] = 'Stopped' + elif ('abandoned' in p_out): + service["serviceStatus"] = 'Active Abandoned' + else: + service["serviceStatus"] = 'Running' + + return service + + def save_service_status(self, service): + service= self.get_service_status(service) + self.save_service(service) + + + def get_services_status(self, services): + for service in services: + service= self.get_service_status(service) + + def get_services_status_and_save(self, services): + for service in services: + service= self.get_service_status(service) + service_id_from_db=self.save_service(service) + service['id']=service_id_from_db + + def handle_task(self): + try: + self.logger.debug("Service Management task is started.") + services = self.data['serviceManageParam'] + task_id= self.context.get('task_id') + # setting task id + for srv in services: + srv['task_id']=task_id + srv['agentDn']=self.Ahenk.dn() + srv['isServiceMonitoring']= True + + db_services = self.db_service.select('service', '*','task_id=' +str(task_id)) + + if len(db_services) < 1: + self.get_services_status_and_save(services) + + stopped_services = '' + for servc in services: + if servc['serviceStatus'] == 'Stopped': + stopped_services += servc['serviceName'] + ' ,' + + if stopped_services != '': + self.send_mail(stopped_services, services) + else: + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Servis izleme görevi başarıyla oluşturuldu.', + data=json.dumps({ + 'Result': 'İşlem Başarı ile gercekleştirildi', + 'services': services, + }), + content_type=self.get_content_type().APPLICATION_JSON.value) + else: + servicesStatusChanged =[] + self.get_services_status(services) + for srv in services: + isExist=False; + for srvDb in db_services: + if srv['serviceName'] == srvDb[1]: + isExist=True + if isExist ==False: + srv=self.get_service_status(srv) + self.save_service(srv) + + + for srv in services: + for srvDb in db_services: + srvDbList=list(srvDb) + if srv['serviceName'] == srvDb[1] and srv['serviceStatus'] != srvDbList[2]: + srvDbList[2]=srv['serviceStatus'] + self.save_service_list(srvDbList) + servicesStatusChanged.append(srv) + + if len(servicesStatusChanged)>0: + + stopped_services='' + for servc in servicesStatusChanged: + if servc['serviceStatus']== 'Stopped' : + stopped_services += servc['serviceName'] + ' ,' + + if stopped_services != '': + + self.send_mail(stopped_services,servicesStatusChanged) + + else: + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='Servis izleme görevi başarıyla oluşturuldu.', + data=json.dumps({ + 'Result': 'İşlem Başarı ile gercekleştirildi', + 'services': servicesStatusChanged + }), + content_type=self.get_content_type().APPLICATION_JSON.value) + else: + self.context.create_response(code=None, + message='Servis izleme görevi başarıyla oluşturuldu.', + data=None, + content_type=self.get_content_type().APPLICATION_JSON.value) + + + except Exception as e: + self.logger.error(str(e)) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='Servis yönetimi sırasında bir hata oluştu: {0}'.format( + str(e))) + + +def handle_task(task, context): + plugin = ServiceManagement(task, context) + plugin.handle_task() diff --git a/src/plugins/sudoers/main.py b/src/plugins/sudoers/main.py new file mode 100644 index 0000000..31c4168 --- /dev/null +++ b/src/plugins/sudoers/main.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Mine Dogan + + +def info(): + inf = dict() + inf['name'] = 'sudoers' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = '' + inf['task'] = False + inf['user_oriented'] = True + inf['machine_oriented'] = False + inf['developer'] = '' + + return inf diff --git a/src/plugins/sudoers/policy.py b/src/plugins/sudoers/policy.py new file mode 100644 index 0000000..881e9fc --- /dev/null +++ b/src/plugins/sudoers/policy.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Mine Dogan + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class GrantSudoAccess(AbstractPlugin): + def __init__(self, data, context): + super(AbstractPlugin, self).__init__() + self.data = data + self.context = context + self.sudoer_line = '{0} ALL = NOPASSWD : /usr/bin/apt-get, /usr/bin/apt , /usr/bin/aptitude' + self.sudoer_file_path = '/etc/sudoers.d/{0}_sudoers' + self.logger = self.get_logger() + + def handle_policy(self): + + username = self.context.get('username') + + try: + if username is not None: + json_data = json.loads(self.data) + + if str(json_data['privilege']) == 'True': + if self.is_exist(self.sudoer_file_path): + self.logger.debug('User sudoers is exist privilege to {}'.format(username)) + else: + self.create_file(self.sudoer_file_path.format(username)) + + sudoer_data = self.read_file(self.sudoer_file_path.format(username)) + self.write_file(self.sudoer_file_path.format(username), sudoer_data.replace(self.sudoer_line.format(username), + '') + '\n' + self.sudoer_line.format(username) + '\n') + + self.logger.debug('User sudoers set privilege to {0}.'.format(username)) + + self.logger.debug('Creating response...') + self.context.create_response(self.get_message_code().POLICY_PROCESSED.value, + 'User sudoers set privilege to {} successfully.'.format(username)) + + elif str(json_data['privilege']) == 'False': + + if self.is_exist(self.sudoer_file_path.format(username)): + self.delete_file(self.sudoer_file_path.format(username)) + self.logger.debug('User sudoers removed privilege from {0}.'.format(username)) + self.logger.debug('Creating response...') + self.context.create_response(self.get_message_code().POLICY_PROCESSED.value, + 'User sudoers removed privilege from {0} successfully.'.format(username)) + else: + self.logger.debug("{0} user's privilege file not found".format(username)) + + else: + self.context.create_response(self.get_message_code().POLICY_PROCESSED.value, 'Missing parameter error.') + + self.logger.debug('Sudoers profile is handled successfully.') + else: + self.logger.error('Username parameter is missing.') + self.context.create_response(self.get_message_code().POLICY_ERROR.value, 'Username is missing') + + except Exception as e: + self.logger.error('A problem occurred while handling sudoers profile: {0}'.format(str(e))) + self.context.create_response(self.get_message_code().POLICY_ERROR.value, + 'A problem occurred while handling sudoers profile: {0}'.format(str(e))) + + +def handle_policy(profile_data, context): + quota = GrantSudoAccess(profile_data, context) + quota.handle_policy() diff --git a/src/plugins/sudoers/safe.py b/src/plugins/sudoers/safe.py new file mode 100644 index 0000000..8d80b67 --- /dev/null +++ b/src/plugins/sudoers/safe.py @@ -0,0 +1,24 @@ +from base.plugin.abstract_plugin import AbstractPlugin + + +class Safe(AbstractPlugin): + def __init__(self, context): + super(Safe, self).__init__() + self.context = context + self.username = str(context.get_username()) + self.logger = self.get_logger() + self.sudoer_file_path = '/etc/sudoers.d/{0}_sudoers' + self.logger.debug('Parameters were initialized.') + + def handle_safe_mode(self): + username = self.context.get('username') + if self.is_exist(self.sudoer_file_path.format(username)): + self.delete_file(self.sudoer_file_path.format(username)) + self.logger.debug('User sudoers removed privilege from {0}.'.format(username)) + + else: + self.logger.debug("{0} user's privilege file not found".format(username)) + +def handle_mode(context): + init = Safe(context) + init.handle_safe_mode() diff --git a/src/plugins/usb/init.py b/src/plugins/usb/init.py new file mode 100644 index 0000000..9870fed --- /dev/null +++ b/src/plugins/usb/init.py @@ -0,0 +1,34 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Init(AbstractPlugin): + def __init__(self, context): + super(Init, self).__init__() + self.context = context + self.logger = self.get_logger() + + self.script = '/bin/bash ' + self.Ahenk.plugins_path() + 'usb/scripts/{0}' + + self.logger.debug('Parameters were initialized.') + + def handle_init_mode(self): + self.execute(self.script.format('ENABLED_webcam.sh'), result=True) + self.logger.debug('Enabled webcam.') + + self.execute(self.script.format('ENABLED_printer.sh'), result=True) + self.logger.debug('Enabled printer.') + + self.execute(self.script.format('ENABLED_usbstorage.sh'), result=True) + self.logger.debug('Enabled usb storage.') + + self.execute(self.script.format('ENABLED_usbhid.sh'), result=True) + self.logger.debug('Enabled usb hid.') + + +def handle_mode(context): + init = Init(context) + init.handle_init_mode() diff --git a/src/plugins/usb/logout.py b/src/plugins/usb/logout.py new file mode 100644 index 0000000..47899a9 --- /dev/null +++ b/src/plugins/usb/logout.py @@ -0,0 +1,25 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Logout(AbstractPlugin): + def __init__(self, context): + super(Logout, self).__init__() + self.context = context + self.logger = self.get_logger() + + self.logger.debug('Parameters were initialized.') + + def handle_logout_mode(self): + if self.is_exist("/etc/udev/rules.d/99-whitelist.rules"): + self.delete_file("/etc/udev/rules.d/99-whitelist.rules") + if self.is_exist("/etc/udev/rules.d/99-blacklist.rules"): + self.delete_file("/etc/udev/rules.d/99-blacklist.rules") + + +def handle_mode(context): + logout = Logout(context) + logout.handle_logout_mode() \ No newline at end of file diff --git a/src/plugins/usb/main.py b/src/plugins/usb/main.py new file mode 100644 index 0000000..bd67b79 --- /dev/null +++ b/src/plugins/usb/main.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +def info(): + inf = dict() + inf['name'] = 'usb' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = 'USB plugin provides to managing usb devices like printer, mouse etc.' + inf['task'] = True + inf['user_oriented'] = False + inf['machine_oriented'] = True + inf['developer'] = 'mine.dogan@agem.com.tr' + + return inf diff --git a/src/plugins/usb/manage-usb.py b/src/plugins/usb/manage-usb.py new file mode 100644 index 0000000..4dd4170 --- /dev/null +++ b/src/plugins/usb/manage-usb.py @@ -0,0 +1,90 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +from base.plugin.abstract_plugin import AbstractPlugin + + +class ManageUsb(AbstractPlugin): + def __init__(self, task, context): + super(AbstractPlugin, self).__init__() + self.task = task + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + if self.has_attr_json(task, 'webcam') is True: + self.webcam = self.task['webcam'] + + if self.has_attr_json(task, 'mouseKeyboard') is True: + self.mouse_keyboard = self.task['mouseKeyboard'] + + if self.has_attr_json(task, 'printer') is True: + self.printer = self.task['printer'] + + if self.has_attr_json(task, 'storage') is True: + self.storage = self.task['storage'] + + self.script = '/bin/bash ' + self.Ahenk.plugins_path() + 'usb/scripts/{0}' + + self.logger.debug('Parameters were initialized.') + + def handle_task(self): + try: + self.logger.debug('Changing permissions...') + + if self.has_attr_json(self.task, 'webcam') is True: + if self.webcam == '1': + self.execute(self.script.format('ENABLED_webcam.sh'), result=True) + elif self.webcam == '0': + self.execute(self.script.format('DISABLED_webcam.sh'), result=True) + + self.logger.debug('Applied permission change for parameter "webcam"') + else: + self.logger.debug('Task has no parameter "webcam"') + + if self.has_attr_json(self.task, 'printer') is True: + if self.printer == '1': + self.execute(self.script.format('ENABLED_printer.sh'), result=True) + elif self.printer == '0': + self.execute(self.script.format('DISABLED_printer.sh'), result=True) + + self.logger.debug('Applied permission change for parameter "printer"') + else: + self.logger.debug('Task has no parameter "printer"') + + if self.has_attr_json(self.task, 'storage') is True: + if self.storage == '1': + self.execute(self.script.format('ENABLED_usbstorage.sh'), result=True) + elif self.storage == '0': + self.execute(self.script.format('DISABLED_usbstorage.sh'), result=True) + + self.logger.debug('Applied permission change for parameter "storage"') + else: + self.logger.debug('Task has no parameter "storage"') + + if self.has_attr_json(self.task, 'mouseKeyboard') is True: + if self.mouse_keyboard == '1': + self.execute(self.script.format('ENABLED_usbhid.sh'), result=True) + elif self.mouse_keyboard == '0': + self.execute(self.script.format('DISABLED_usbhid.sh'), result=True) + + self.logger.debug('Applied permission change for parameter "mouseKeyboard"') + else: + self.logger.debug('Task has no parameter "mouseKeyboard"') + + self.logger.debug('Applied permission changes.') + + self.logger.info('USB task is handled successfully') + self.context.create_response(code=self.message_code.TASK_PROCESSED.value, + message='USB izinleri başarıyla güncellendi.') + + except Exception as e: + self.logger.error('A problem occured while handling USB task: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.TASK_ERROR.value, + message='USB izinleri güncellenirken bir hata oluştu.') + + +def handle_task(task, context): + manage = ManageUsb(task, context) + manage.handle_task() diff --git a/src/plugins/usb/policy.py b/src/plugins/usb/policy.py new file mode 100644 index 0000000..53bb4af --- /dev/null +++ b/src/plugins/usb/policy.py @@ -0,0 +1,268 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author:Mine DOGAN + +import json + +from base.plugin.abstract_plugin import AbstractPlugin + + +class Usb(AbstractPlugin): + def __init__(self, data, context): + super(Usb, self).__init__() + self.data = data + self.context = context + self.logger = self.get_logger() + self.message_code = self.get_message_code() + + self.parameters = json.loads(self.data) + self.script = '/bin/bash ' + self.Ahenk.plugins_path() + 'usb/scripts/{0}' + self.script_path = self.Ahenk.plugins_path() + 'usb/scripts/{0}' + self.items = [] + + self.command_vendor = "grep -lw '{0}' /sys/bus/usb/devices/*/manufacturer | grep -o -P '.{{0,}}/.{{0,0}}'" + self.command_model = "grep -lw '{0}' {1}product" + self.command_serial = "grep -lw '{0}' {1}serial" + self.command_authorized = "echo '{0}' > {1}authorized" + + self.command_serial_is_exist = 'if test -e {0}serial; then echo "exist"; else echo "not found"; fi' + + self.logger.debug('Parameters were initialized.') + + def handle_policy(self): + try: + self.logger.debug('Permissions will be applied for profile.') + self.manage_permissions() + if self.has_attr_json(self.parameters, 'items') is True: + self.items = self.parameters['items'] + self.logger.debug('Blacklist/Whitelist will be created for profile.') + if self.has_attr_json(self.parameters, 'type') is True: + self.logger.debug('BlackList Whitelist will be created....') + self.create_blacklist_whitelist() + + self.logger.info('USB profile is handled successfully.') + self.context.create_response(code=self.message_code.POLICY_PROCESSED.value, + message='USB izinleri başarıyla güncellendi.') + + except Exception as e: + self.logger.error('A problem occurred while handling USB policy. Error Message: {0}'.format(str(e))) + self.context.create_response(code=self.message_code.POLICY_ERROR.value, + message='USB politikası uygulanırken bir hata oluştu: {0}'.format(str(e))) + + def manage_permissions(self): + + self.logger.debug('Changing permissions...') + + if self.has_attr_json(self.parameters, 'webcam') is True: + if self.parameters['webcam'] == '1': + self.execute(self.script.format('ENABLED_webcam.sh'), result=True) + elif self.parameters['webcam'] == '0': + self.execute(self.script.format('DISABLED_webcam.sh'), result=True) + + self.logger.debug('Applied permission change for parameter "webcam"') + else: + self.logger.debug('Data has no parameter "webcam"') + + if self.has_attr_json(self.parameters, 'printer') is True: + if self.parameters['printer'] == '1': + self.execute(self.script.format('ENABLED_printer.sh'), result=True) + elif self.parameters['printer'] == '0': + self.execute(self.script.format('DISABLED_printer.sh'), result=True) + + self.logger.debug('Applied permission change for parameter "printer"') + else: + self.logger.debug('Data has no parameter "printer"') + + if self.has_attr_json(self.parameters, 'storage') is True: + if self.parameters['storage'] == '1': + self.execute(self.script.format('ENABLED_usbstorage.sh'), result=True) + elif self.parameters['storage'] == '0': + self.execute(self.script.format('DISABLED_usbstorage.sh'), result=True) + + self.logger.debug('Applied permission change for parameter "storage"') + else: + self.logger.debug('Data has no parameter "storage"') + + if self.has_attr_json(self.parameters, 'mouseKeyboard') is True: + if self.parameters['mouseKeyboard'] == '1': + self.execute(self.script.format('ENABLED_usbhid.sh'), result=True) + elif self.parameters['mouseKeyboard'] == '0': + self.execute(self.script.format('DISABLED_usbhid.sh'), result=True) + + self.logger.debug('Applied permission change for parameter "mouseKeyboard"') + else: + self.logger.debug('Data has no parameter "mouseKeyboard"') + + self.logger.debug('Permissions were applied.') + + def organize_rule_files(self, is_whitelist): + if is_whitelist == 0: + if self.is_exist("/etc/udev/rules.d/99-whitelist.rules"): + self.delete_file("/etc/udev/rules.d/99-whitelist.rules") + self.execute('> /etc/udev/rules.d/99-blacklist.rules') + else: + if self.is_exist("/etc/udev/rules.d/99-blacklist.rules"): + self.delete_file("/etc/udev/rules.d/99-blacklist.rules") + self.execute('> /etc/udev/rules.d/99-whitelist.rules') + + def write_whitelist_line(self, vendor, model, serial_number, is_first_line): + command_blackandwhitelist = 'echo ' + "'" + symbol = '=' + authorized = '1' + if is_first_line is True: + command_blackandwhitelist = 'ex -sc ' + "'1i|" + symbol = '!' + authorized = '0' + command_blackandwhitelist += 'ACTION==\"add|change\" , SUBSYSTEM==\"usb\" , ' + if vendor is not None and len(vendor) > 0: + command_blackandwhitelist += ' ATTR{manufacturer}' + symbol + '=\"' + vendor + '\" , ' + if model is not None and len(model) > 0: + command_blackandwhitelist += ' ATTR{product}' + symbol + '=\"' + model + '\" , ' + if serial_number is not None and len(serial_number) > 0: + command_blackandwhitelist += ' ATTR{serial}' + symbol + '=\"' + serial_number + '\" , ' + command_blackandwhitelist += ' ATTR{authorized}=\"' + authorized + '\" ' + "'" + if is_first_line is False: + command_blackandwhitelist += ' >> ' + else: + command_blackandwhitelist += ' -cx ' + command_blackandwhitelist += '/etc/udev/rules.d/99-whitelist.rules' + self.logger.debug(command_blackandwhitelist) + self.write_rule_line(command_blackandwhitelist) + + def write_rule_line(self, command): + p_result_code, p_out, p_err = self.execute(command) + if p_result_code == 0: + self.logger.debug('Rule line is added successfully') + elif p_result_code != 0: + self.logger.debug('Error while adding rule line to /etc/udev/rules.d/ , Error message : {0}'.format(p_err)) + + def create_rule_line(self, vendor, model, serial_number, is_whitelist): + if is_whitelist == 0: + command_blackandwhitelist = 'echo ' + "'" + 'ACTION ==\"add|change\" , SUBSYSTEM==\"usb\" , ' + if vendor is not None and len(vendor) > 0: + command_blackandwhitelist += ' ATTR{manufacturer}==\"' + vendor + '\" , ' + if model is not None and len(model) > 0: + command_blackandwhitelist += ' ATTR{product}==\"' + model + '\" , ' + if serial_number is not None and len(serial_number) > 0: + command_blackandwhitelist += ' ATTR{serial}==\"' + serial_number + '\" , ' + command_blackandwhitelist += ' ATTR{authorized}=\"0\" ' + "'" + '>> /etc/udev/rules.d/99-blacklist.rules' + self.write_rule_line(command_blackandwhitelist) + else: + self.write_whitelist_line(vendor, model, serial_number, True) + self.write_whitelist_line(vendor, model, serial_number, False) + + def create_blacklist_whitelist(self): + self.logger.debug('usb storage will be enabled') + self.execute(self.script.format('ENABLED_usbstorage.sh'), result=True) + self.logger.debug('usb storage enabled') + if self.parameters['type'] == 'blacklist': + is_whitelist = 0 + else: + is_whitelist = 1 + self.logger.debug('Rule files are organizing....') + self.organize_rule_files(is_whitelist) + self.logger.debug('Rule files are organized') + + for item in self.items: + item_parameters = json.loads(str(json.dumps(item))) + vendor = item_parameters['vendor'] + model = item_parameters['model'] + serial_number = item_parameters['serialNumber'] + + self.create_rule_line(vendor, model, serial_number, is_whitelist) + + self.logger.debug('vendor, model and serial number is set....') + self.logger.debug(self.command_vendor.format(vendor)) + result_code, p_out, p_err = self.execute(self.command_vendor.format(vendor), result=True) + folder_list = str(p_out).split('\n') + folder_list.pop() + + if p_out == '' and vendor != '': + self.logger.debug('Device has not been found because of vendor. Vendor: {0}'.format(vendor)) + + if vendor == '': + folder_list = [] + folder_list.append('/sys/bus/usb/devices/*/') + + for folder in folder_list: + + result_code, p_out, p_err = self.execute(self.command_model.format(model, folder), result=True) + + if p_out == '' and model != '': + self.logger.debug( + 'Device model has not been found in this directory. Directory: {0}, Vendor: {1}, Model: {2}'.format( + folder, vendor, model)) + + else: + model_folder_list = str(p_out).split('\n') + model_folder_list.pop() + + if p_out == '': + model_folder_list.append(folder) + + if vendor == '' and model == '': + model_folder_list = [] + model_folder_list.append('/sys/bus/usb/devices/*/') + + for model_folder in model_folder_list: + if 'product' in model_folder: + model_folder = model_folder.strip('product') + + if model_folder != '/sys/bus/usb/devices/*/': + result_code, p_out, p_err = self.execute(self.command_serial_is_exist.format(model_folder), + result=True) + + if 'exist' in p_out or model_folder == '/sys/bus/usb/devices/*/': + result_code, p_out, p_err = self.execute( + self.command_serial.format(serial_number, model_folder), + result=True) + if p_out == '' and serial_number != '': + self.logger.debug( + 'Device serial number has not been found in this directory. Directory: {0}, Vendor: {1}, Model: {2}, Serial Number: {3}'.format( + model_folder, vendor, + model, serial_number)) + else: + serial_folder_list = str(p_out).split('\n') + serial_folder_list.pop() + + if p_out == '': + serial_folder_list.append(model_folder) + + for serial_folder in serial_folder_list: + serial_folder = serial_folder.strip('serial') + if self.parameters['type'] == 'whitelist': + self.execute(self.command_authorized.format('1', serial_folder), result=True) + self.logger.debug( + 'Enabled the device. Directory: {0}, Vendor: {1}, Model: {2}, Serial Number: {3}'.format( + serial_folder, vendor, model, serial_number)) + elif self.parameters['type'] == 'blacklist': + self.execute(self.command_authorized.format('0', serial_folder), result=True) + self.logger.debug( + 'Disabled the device. Directory: {0}, Vendor: {1}, Model: {2}, Serial Number: {3}'.format( + serial_folder, vendor, model, serial_number)) + + elif 'not found' in p_out: + dir = '' + if model != '': + dir = model_folder + elif vendor != '': + dir = folder + + if self.parameters['type'] == 'whitelist': + self.execute(self.command_authorized.format('1', dir), result=True) + self.logger.debug( + 'Enabled the device. Directory: {0}, Vendor: {1}, Model: {2}, Serial Number: {3}'.format( + dir, vendor, model, serial_number)) + elif self.parameters['type'] == 'blacklist': + self.execute(self.command_authorized.format('0', dir), result=True) + self.logger.debug( + 'Disabled the device. Directory: {0}, Vendor: {1}, Model: {2}, Serial Number: {3}'.format( + dir, vendor, model, serial_number)) + + self.execute('udevadm control --reload-rules') + self.logger.debug('Blacklist/Whitelist was created.') + + +def handle_policy(profile_data, context): + manage = Usb(profile_data, context) + manage.handle_policy() diff --git a/src/plugins/usb/scripts/DISABLED_printer.sh b/src/plugins/usb/scripts/DISABLED_printer.sh new file mode 100755 index 0000000..62a8a08 --- /dev/null +++ b/src/plugins/usb/scripts/DISABLED_printer.sh @@ -0,0 +1,13 @@ +#!/bin/bash +var=$(lsmod | awk '{print $1}'| grep usblp) + +service cups stop + +if [ -z "$var" ] +then +echo "USB printer devices are already blocked" +else +rmmod usblp +sleep 2 +fi + diff --git a/src/plugins/usb/scripts/DISABLED_usbhid.sh b/src/plugins/usb/scripts/DISABLED_usbhid.sh new file mode 100755 index 0000000..0105c05 --- /dev/null +++ b/src/plugins/usb/scripts/DISABLED_usbhid.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +var=$(lsmod | grep usbhid) + +if [ -z "$var" ] +then +echo "USB HID devices are already blocked" +else +for device in /sys/bus/usb/drivers/usbhid/* ; do + if [[ $device == *:* ]] + then + echo "${device##*/}" + echo "${device##*/}" | tee -a /sys/bus/usb/drivers/usbhid/unbind + fi +done + +sleep 2 +rmmod usbhid +echo blacklist usbhid >> /etc/modprobe.d/blockusbhid.conf +fi + +var=$(lsmod | grep psmouse) + +if [ -z "$var" ] +then +echo "psmouse is already blocked" +else +rmmod psmouse +echo blacklist psmouse >> /etc/modprobe.d/blockusbhid.conf +fi + +#for ld in `who | grep $1 | egrep -o " \(:[0-9]+\)" | egrep -o ":[0-9]+"`; do +# export DISPLAY="$ld" +# for hid in `sudo -u $1 xinput --list | grep slave | grep -o 'id=[0-9]\+' | grep -o '[0-9]\+'`; do + # sudo -u $1 xinput set-int-prop $hid "Device Enabled" 8 0 + #done +#done diff --git a/src/plugins/usb/scripts/DISABLED_usbstorage.sh b/src/plugins/usb/scripts/DISABLED_usbstorage.sh new file mode 100755 index 0000000..d875243 --- /dev/null +++ b/src/plugins/usb/scripts/DISABLED_usbstorage.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +var=$(lsmod | awk '{print $1}'| grep usb_storage) + +if [ -z "$var" ] +then +echo "USB storage devices are already blocked" +else +rm /etc/modprobe.d/blockusbstorages.conf +for device in /sys/bus/usb/drivers/usb-storage/* ; do + if [[ $device == *:* ]] + then + echo "${device##*/}" + echo "${device##*/}" | tee -a /sys/bus/usb/drivers/usb-storage/unbind + fi +done + +sleep 2 + +for usb_dev in /dev/disk/by-id/usb-*; do + dev=$(readlink -f $usb_dev) + grep -q ^$dev /proc/mounts && umount -f $dev +done + +sleep 2 + +var=$(lsmod | grep usb_storage | awk '{print $4}') + +if [ ! -z "$var" ] +then +IFS=',' read -ra deps <<< "$var" +for i in "${deps[@]}"; do + modprobe -r "$i" + echo blacklist "$i" >> /etc/modprobe.d/blockusbstorages.conf +done +fi + +sleep 2 + +modprobe -r usb_storage +echo blacklist usb_storage >> /etc/modprobe.d/blockusbstorages.conf +sleep 2 +fi + + + diff --git a/src/plugins/usb/scripts/DISABLED_webcam.sh b/src/plugins/usb/scripts/DISABLED_webcam.sh new file mode 100755 index 0000000..4f24bd4 --- /dev/null +++ b/src/plugins/usb/scripts/DISABLED_webcam.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +var=$(lsof -t /dev/video0) + +if [ -z "$var" ] +then +echo "Webcam is not in use" +else +kill -9 "$var" +sleep 2 +fi + +var=$(lsmod | awk '{print $1}'| grep uvcvideo) + +if [ -z "$var" ] +then +echo "Webcam is already blocked" +else +rmmod uvcvideo +sleep 2 +fi + + diff --git a/src/plugins/usb/scripts/ENABLED_printer.sh b/src/plugins/usb/scripts/ENABLED_printer.sh new file mode 100755 index 0000000..59571f9 --- /dev/null +++ b/src/plugins/usb/scripts/ENABLED_printer.sh @@ -0,0 +1,3 @@ +#!/bin/bash +modprobe usblp +service cups start \ No newline at end of file diff --git a/src/plugins/usb/scripts/ENABLED_usbhid.sh b/src/plugins/usb/scripts/ENABLED_usbhid.sh new file mode 100755 index 0000000..ecd3110 --- /dev/null +++ b/src/plugins/usb/scripts/ENABLED_usbhid.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +modprobe usbhid +modprobe psmouse + + +#for ld in `who | grep $1 | egrep -o " \(:[0-9]+\)" | egrep -o ":[0-9]+"`; do +# export DISPLAY="$ld" +# for hid in `sudo -u $1 xinput --list | grep slave | grep -o 'id=[0-9]\+' | grep -o '[0-9]\+'`; do +# sudo -u $1 xinput set-int-prop $hid "Device Enabled" 8 1 +# done +#done + +echo "" > /etc/modprobe.d/blockusbhid.conf diff --git a/src/plugins/usb/scripts/ENABLED_usbstorage.sh b/src/plugins/usb/scripts/ENABLED_usbstorage.sh new file mode 100755 index 0000000..00b19e2 --- /dev/null +++ b/src/plugins/usb/scripts/ENABLED_usbstorage.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +while read line +do + IFS=' ' read -a module <<< "$line" + modprobe "${module[1]}" +done < /etc/modprobe.d/blockusbstorages.conf + +echo "" | tee -a /etc/modprobe.d/blockusbstorages.conf + +modprobe usb_storage + +for usb_dev in /dev/disk/by-id/usb-*; do + dev=$(readlink -f $usb_dev) + grep -q ^$dev /proc/mounts && mount -f $dev +done \ No newline at end of file diff --git a/src/plugins/usb/scripts/ENABLED_webcam.sh b/src/plugins/usb/scripts/ENABLED_webcam.sh new file mode 100755 index 0000000..f649f99 --- /dev/null +++ b/src/plugins/usb/scripts/ENABLED_webcam.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +modprobe uvcvideo \ No newline at end of file diff --git a/src/plugins/user-privilege/init.py b/src/plugins/user-privilege/init.py new file mode 100644 index 0000000..b2dbea4 --- /dev/null +++ b/src/plugins/user-privilege/init.py @@ -0,0 +1,94 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +""" +Style Guide is PEP-8 +https://www.python.org/dev/peps/pep-0008/ +""" + +import glob +import json +import os + +from base.plugin.abstract_plugin import AbstractPlugin + + +class UserPrivilegeInitMode(AbstractPlugin): + def __init__(self, context): + super(AbstractPlugin, self).__init__() + self.context = context + self.logger = self.get_logger() + + def handle_for_this_user(self, username): + + p_path = self.Ahenk.plugins_path() + + privilege_file = p_path + 'user-privilege/privilege.changes/' + username + '.changes' + + if self.is_exist(privilege_file): + self.logger.debug('Reading privilege_file: ' + privilege_file) + with open(privilege_file) as data_file: + self.logger.debug('Creating object from JSON data file.') + data = json.load(data_file) + + command_path_list = data['command_path_list'] + added_user_list = data['added_user_list'] + deleted_user_list = data['deleted_user_list'] + + if len(command_path_list) != 0: + self.logger.debug('Removing wrapper files and renaming original files.') + + for command_path in command_path_list: + if os.path.exists(command_path + '-ahenk'): + self.logger.debug('Executing: ' + '"rm ' + command_path + '"') + self.execute('rm ' + command_path) + self.logger.debug( + 'Executing: ' + '"mv ' + command_path + '-ahenk ' + command_path + '"') + self.execute('mv ' + command_path + '-ahenk ' + command_path) + else: + self.logger.debug( + 'File will not be deleted because ' + command_path + 'does not exists.') + + if len(added_user_list) != 0: + self.logger.debug('Removing user from groups that it has been added in advance.') + + for group_name in added_user_list: + self.logger.debug( + 'Executing: ' + '"deluser ' + str(username) + ' ' + group_name + '"') + self.execute('deluser ' + str(username) + ' ' + group_name) + + if len(deleted_user_list) != 0: + self.logger.debug('Adding user to groups that it has been removed in advance.') + + for group_name in deleted_user_list: + self.logger.debug( + 'Executing: ' + '"adduser ' + str(username) + ' ' + group_name + '"') + self.execute('adduser ' + str(username) + ' ' + group_name) + else: + self.logger.debug('Changes file not found for {} user.'.format(username)) + + def handle_init_mode(self): + self.logger.debug('Handling init mode.') + + changes_file_arr = self.Ahenk.plugins_path() + 'user-privilege/privilege.changes/*.changes' + change_files = glob.glob(changes_file_arr) + + if change_files is not None and len(change_files) > 0: + self.logger.debug('Some user changes found.') + for file in change_files: + tmp = file.replace(self.Ahenk.plugins_path() + 'user-privilege/privilege.changes/', '') + tmp = tmp.replace('.changes', '') + self.logger.debug('Handling init for user {0}'.format(tmp)) + try: + self.handle_for_this_user(tmp) + self.logger.debug('Handled init for user {0}'.format(tmp)) + except Exception as e: + self.logger.error( + 'A problem occurred while handling init action for user {0}. Error Message: {1}'.format( + tmp, str(e))) + else: + self.logger.debug('Changes files not found.') + + +def handle_mode(context): + user_privilege = UserPrivilegeInitMode(context) + user_privilege.handle_init_mode() diff --git a/src/plugins/user-privilege/main.py b/src/plugins/user-privilege/main.py new file mode 100644 index 0000000..759ab5b --- /dev/null +++ b/src/plugins/user-privilege/main.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +def info(): + inf = dict() + inf['name'] = 'user-privilege' + inf['version'] = '1.0.0' + inf['support'] = 'debian' + inf['description'] = '' + inf['task'] = False + inf['user_oriented'] = True + inf['machine_oriented'] = False + inf['developer'] = '' + + return inf diff --git a/src/plugins/user-privilege/policy.py b/src/plugins/user-privilege/policy.py new file mode 100644 index 0000000..9ddbb55 --- /dev/null +++ b/src/plugins/user-privilege/policy.py @@ -0,0 +1,371 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Caner Feyzullahoglu +""" +Style Guide is PEP-8 +https://www.python.org/dev/peps/pep-0008/ +""" +import json +import os + +from base.plugin.abstract_plugin import AbstractPlugin + + +class UserPrivilege(AbstractPlugin): + def __init__(self, profile_data, context): + super(AbstractPlugin, self).__init__() + self.profile_data = profile_data + self.context = context + self.logger = self.get_logger() + + # self.logger = self.context.logger + + self.polkit_action_folder_path = '/usr/share/polkit-1/actions/' + self.polkit_pkla_folder_path = '/etc/polkit-1/localauthority/50-local.d/' + self.default_action_pref = 'tr.org.pardus.mys.pkexec.' + + # self.permission_file_path = self.agent_config.get("UserPolicyPlugin", "userpolicyplugin.policyfile") + # The below line was like above line at old version. + self.permission_file_path = '/etc/ahenk/userpolicies' + # "permission_file"_path was being taken from a config file which has been created by "agentconfig.py" + # but this py is not present in new version. So I created it as a static string. + + def limit_ahenk(self, item): + + if self.has_attr_json(item, 'cpu') and item['cpu'] is not None and item['cpu'] is not '': + self.logger.debug('Limiting ahenk cpu usage. Cpu limit value: {0}'.format(item['cpu'])) + self.execute('cpulimit -p {0} -l {1} -z &'.format(str(self.Ahenk.get_pid_number()), str(item['cpu'])), + result=False) + self.logger.debug('Limited ahenk cpu usage. Cpu limit value: {0}'.format(item['cpu'])) + + def handle_policy(self): + print('Handling policy') + self.logger.debug('Handling policy.') + # Get username. + # It is actually user UID in LDAP. The message being created in PolicySubscriberImpl by using + # MessageFactoryImpl at server side. + # At agent side Plugin.py takes a message from queue as an item, finds related plugin module by using + # Scope (with parameters from the item such as plugin name and plugin version), puts "username" to context and + # triggers "handle_policy" method of related plugin. + username = self.context.get('username') + + try: + result_message = '' + + if username is not None: + self.logger.debug('Getting profile data.') + data = json.loads(self.profile_data) + privilege_items = data['items'] + + # Lists that will keep user names for cleaning on logout + add_user_list = list() + del_user_list = list() + # List that will keep command paths + command_path_list = list() + + if len(privilege_items) > 0: + self.logger.debug('Iterating over privilege items.') + + for item in privilege_items: + command_path = item['cmd'].strip() + if command_path == "/opt/ahenk/ahenkd": + self.limit_ahenk(item) + continue + + if self.is_exist(command_path) is False: + self.logger.warning( + '{0} command path not found. User privilege execution will not processed for this command.'.format( + command_path)) + continue + + polkit_status = item['polkitStatus'] + + # Create polkit for each item + if polkit_status == 'privileged': + + self.logger.debug('Parsing command.') + command = str(self.parse_command(command_path)) + + action_id = self.default_action_pref + command + + if not os.path.exists(action_id + '.policy'): + self.logger.debug( + 'Creating action; command: ' + command + ', action_id: ' + action_id + + ', command_path: ' + command_path) + self.create_action(command, action_id, command_path) + + self.logger.debug( + 'Executing: "getent group ' + command + ' || groupadd ' + command + '"') + self.execute('getent group ' + command + ' || groupadd ' + command) + + self.logger.debug( + 'Executing: "adduser ' + str(username) + ' ' + command + '"') + self.execute('adduser ' + str(username) + ' ' + command) + + self.logger.debug('Adding command to add_user_list') + add_user_list.append(command) + + if not os.path.exists(action_id + '.pkla'): + self.logger.debug( + 'Creating pkla; command: ' + command + ', action_id: ' + action_id) + self.create_pkla(command, action_id) + + self.logger.debug('Executing: "grep "pkexec" ' + str(command_path) + '"') + (result_code, p_out, p_err) = self.execute('grep "pkexec" ' + str(command_path)) + + if result_code != 0: + # Get resource limit choice + limit_resource_usage = item['limitResourceUsage'] + # Get CPU and memory usage parameters + cpu = item['cpu'] + memory = item['memory'] + + # Create wrapper command with resource limits + self.logger.debug( + 'Creating wrapper command; command_path: ' + command_path + + ', limit_resource_usage: ' + str(limit_resource_usage) + ', cpu: ' + + str(cpu) + ', memory: ' + str(memory)) + (wrapper_result, p_out, p_err) = self.create_wrapper_command(command_path, + polkit_status, + limit_resource_usage, + cpu, memory, + command_path_list) + + if wrapper_result == 0: + self.logger.debug('Wrapper created successfully.') + self.logger.debug('Adding item result to result_message.') + result_message += command_path + ' | Ayrıcalıklı | Başarılı, ' + else: + self.logger.debug('Adding item result to result_message.') + result_message += command_path + ' | Ayrıcalıklı | Başarısız, ' + + elif polkit_status == 'unprivileged': + + self.logger.debug('Parsing command.') + command = str(self.parse_command(command_path)) + + action_id = self.default_action_pref + command + + if not os.path.exists(action_id + '.policy'): + self.logger.debug( + 'Creating action; command: ' + command + ', action_id: ' + action_id + + ', command_path: ' + command_path) + self.create_action(command, action_id, command_path) + + self.logger.debug( + 'Executing: "getent group ' + command + ' || groupadd ' + command + '"') + self.execute('getent group ' + command + ' || groupadd ' + command) + + self.logger.debug( + 'Executing: "deluser ' + str(username) + ' ' + command + '"') + self.execute('deluser ' + str(username) + ' ' + command) + + self.logger.debug('Adding command to del_user_list') + del_user_list.append(command) + + if not os.path.exists(action_id + '.pkla'): + self.logger.debug( + 'Creating pkla; command: ' + command + ', action_id: ' + action_id) + self.create_pkla(command, action_id) + + self.logger.debug('Executing: "grep "pkexec" ' + str(command_path) + '"') + (result_code, p_out, p_err) = self.execute('grep "pkexec" ' + str(command_path)) + + if result_code != 0: + # Get resource limit choice + limit_resource_usage = item['limitResourceUsage'] + # Get CPU and memory usage parameters + cpu = item['cpu'] + memory = item['memory'] + + # Create wrapper command with resource limits + self.logger.debug( + 'Creating wrapper command; command_path: ' + command_path + + ', limit_resource_usage: ' + str(limit_resource_usage) + ', cpu: ' + + str(cpu) + ', memory: ' + str(memory)) + (wrapper_result, p_out, p_err) = self.create_wrapper_command(command_path, + polkit_status, + limit_resource_usage, + cpu, memory, + command_path_list) + if wrapper_result == 0: + self.logger.debug('Wrapper created successfully.') + self.logger.debug('Adding item result to result_message.') + result_message += command_path + ' | Ayrıcalıksız | Başarılı, ' + else: + self.logger.debug('Adding item result to result_message.') + result_message += command_path + ' | Ayrıcalıksız | Başarısız, ' + + elif polkit_status == 'na': + + self.logger.debug( + 'polkit_status is: na, no action or pkla will be created.') + + # Get resource limit choice + limit_resource_usage = item['limitResourceUsage'] + # Get CPU and memory usage parameters + cpu = item['cpu'] + memory = item['memory'] + + # Create wrapper command with resource limits + self.logger.debug( + 'Creating wrapper command; command_path: ' + command_path + + ', limit_resource_usage: ' + str(limit_resource_usage) + ', cpu: ' + + str(cpu) + ', memory: ' + str(memory)) + (wrapper_result, p_out, p_err) = self.create_wrapper_command(command_path, polkit_status, + limit_resource_usage, + cpu, memory, command_path_list) + + if wrapper_result == 0: + self.logger.debug('Wrapper created successfully.') + self.logger.debug('Adding item result to result_message.') + result_message += command_path + ' | Ayrıcalıklı | Başarılı, ' + else: + self.logger.debug('Adding item result to result_message.') + result_message += command_path + ' | Ayrıcalıklı | Başarısız, ' + + self.logger.debug('Getting plugin path.') + + p_path = self.Ahenk.plugins_path() + + self.logger.debug('Creating logout files.') + self.create_logout_files(username, p_path, add_user_list, del_user_list, command_path_list) + + self.logger.debug('Creating response.') + self.context.create_response(code=self.get_message_code().POLICY_PROCESSED.value, + message=result_message) + + self.logger.debug('User Privilege profile is handled successfully.') + + else: + self.logger.debug('Creating response.') + self.context.create_response(code=self.get_message_code().POLICY_WARNING.value, + message='Kullanıcı adı olmadan USER PRIVILEGE profili çalıştırılamaz.') + + except Exception as e: + self.context.create_response(code=self.get_message_code().POLICY_ERROR.value, + message='USER PRIVILEGE profili uygulanırken bir hata oluştu.') + self.logger.error( + 'A problem occurred while handling User Privilege profile: {0}'.format(str(e))) + + def parse_command(self, command_path): + splitted_command_str = str(command_path).split('/') + return splitted_command_str[-1] + + def create_action(self, command, command_id, command_path): + command_path += '-ahenk' + action_str = ' \n' \ + ' \n' \ + ' \n' \ + ' \n' \ + ' Please enter the password for this action \n' \ + ' {cmd} \n' \ + ' \n' \ + ' auth_admin \n' \ + ' auth_admin \n' \ + ' auth_admin \n' \ + ' \n' \ + ' {cmd_path} \n' \ + ' true \n' \ + ' \n' \ + ' \n'.format(action_id=command_id, cmd=command, cmd_path=command_path) + + action_file_path = self.polkit_action_folder_path + command_id + '.policy' + action_file = open(action_file_path, 'w') + action_file.write(action_str) + action_file.close() + + def create_pkla(self, command, action_id): + pkla_str = '[Normal Staff Permissions]\n' \ + 'Identity=unix-group:{grp}\n' \ + 'Action={actionId}\n' \ + 'ResultAny=no\n' \ + 'ResultInactive=no\n' \ + 'ResultActive=yes\n'.format(grp=command, actionId=action_id) + + pkla_file_path = self.polkit_pkla_folder_path + action_id + '.pkla' + pkla_file = open(pkla_file_path, 'w') + pkla_file.write(pkla_str) + pkla_file.close() + + def create_wrapper_command(self, command_path, polkit_status, limit_resource_usage, cpu, memory, command_path_list): + self.logger.debug( + 'Executing: "' + 'mv ' + str(command_path) + ' ' + str(command_path) + '-ahenk"') + (result_code, p_out, p_err) = self.execute( + 'mv ' + str(command_path) + ' ' + str(command_path) + '-ahenk') + + if result_code == 0: + + command_path_str = str(command_path).strip() + + line = 'if [ \( "$USER" = "root" \) -o \( "$USER" = "" \) ]; then \n' + str(command_path) + '-ahenk $@' + if limit_resource_usage: + line += '\nelse\n' + line += self.add_resource_limits(command_path, polkit_status, cpu, memory) + else: + if polkit_status == 'na': + line = line + '\nelse\n' + str(command_path) + '-ahenk $@\n' + else: + line += '\nelse\nCOMMAND=\"' + str(command_path) + '-ahenk $@\"\n' + line += 'pkexec --user $USER $COMMAND\n' + line += 'fi' + + self.logger.debug('Writing to newly created file: ' + command_path_str) + self.write_file(command_path_str, line) + self.set_permission(command_path_str, "755") + + self.logger.debug('Command created successfully ' + command_path) + + self.logger.debug('Adding command to command_path_list') + command_path_list.append(command_path) + + return 0, p_out, p_err + else: + self.logger.debug('Wrap could not created ' + command_path) + return 1, p_out, p_err + + def add_resource_limits(self, command_path, polkit_status, cpu, memory): + self.logger.debug('Adding resource limits to wrapper command.') + lines = '' + if cpu and memory is not None: + self.logger.debug('Adding both CPU and memory limits.') + lines = 'ulimit -Sv ' + str(memory) + '\n' + lines += 'COMMAND=\"' + str(command_path) + '-ahenk $@\"\n' + lines += 'nohup pkexec --user $USER $COMMAND &\n' + lines += 'U_PID=$!\n' + lines += 'cpulimit -p $U_PID -l ' + str(cpu) + ' -z\n' + elif cpu is not None: + self.logger.debug('Adding only CPU limit.') + lines = 'COMMAND=\"' + str(command_path) + '-ahenk $@\"\n' + lines += 'nohup pkexec --user $USER $COMMAND &\n' + lines += 'U_PID=$!\n' + lines += 'cpulimit -p $U_PID -l ' + str(cpu) + ' -z\n' + elif memory is not None: + self.logger.debug('Adding only memory limit.') + lines = 'ulimit -Sv ' + str(memory) + '\n' + lines += 'COMMAND=\"' + str(command_path) + '-ahenk $@\"\n' + lines += 'nohup pkexec --user $USER $COMMAND &\n' + + return lines + + def create_logout_files(self, username, path_of_plugin, add_user_list, del_user_list, command_path_list): + path_of_changes = path_of_plugin + 'user-privilege/privilege.changes' + if not os.path.exists(path_of_changes): + self.create_directory(path_of_changes) + + self.logger.debug('Creating JSON data for user privilege changes.') + data = {'added_user_list': add_user_list, 'deleted_user_list': del_user_list, + 'command_path_list': command_path_list} + + path_of_user_changes = path_of_changes + '/' + username + '.changes' + self.logger.debug('Creating file: ' + path_of_user_changes) + with open(path_of_user_changes, 'w') as f: + self.logger.debug('Writing JSON data to: ' + path_of_user_changes) + json.dump(data, f) + + +def handle_policy(profile_data, context): + user_privilege = UserPrivilege(profile_data, context) + user_privilege.handle_policy() diff --git a/src/plugins/user-privilege/safe.py b/src/plugins/user-privilege/safe.py new file mode 100644 index 0000000..41c57b4 --- /dev/null +++ b/src/plugins/user-privilege/safe.py @@ -0,0 +1,73 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Caner Feyzullahoglu +""" +Style Guide is PEP-8 +https://www.python.org/dev/peps/pep-0008/ +""" + +import json +import os +from base.plugin.abstract_plugin import AbstractPlugin + + +class UserPrivilegeSafeMode(AbstractPlugin): + def __init__(self, context): + super(AbstractPlugin, self).__init__() + self.username = str(context.get_username()) + self.context = context + self.logger = self.get_logger() + + def handle_safe_mode(self): + self.logger.debug('Handling safe mode.') + + self.logger.debug('Getting plugin path.') + p_path = self.Ahenk.plugins_path() + privilege_file = p_path + 'user-privilege/privilege.changes/' + self.username + '.changes' + + if self.is_exist(privilege_file): + self.logger.debug('Reading privilege_file: ' + privilege_file) + with open(privilege_file) as data_file: + self.logger.debug('Creating object from JSON data file.') + data = json.load(data_file) + + command_path_list = data['command_path_list'] + added_user_list = data['added_user_list'] + deleted_user_list = data['deleted_user_list'] + + if len(command_path_list) != 0: + self.logger.debug('Removing wrapper files and renaming original files.') + + for command_path in command_path_list: + if os.path.exists(command_path + '-ahenk'): + self.logger.debug('Executing: ' + '"rm ' + command_path + '"') + self.execute('rm ' + command_path) + self.logger.debug( + 'Executing: ' + '"mv ' + command_path + '-ahenk ' + command_path + '"') + self.execute('mv ' + command_path + '-ahenk ' + command_path) + else: + self.logger.debug( + 'File will not be deleted because ' + command_path + 'does not exists.') + + if len(added_user_list) != 0: + self.logger.debug('Removing user from groups that it has been added in advance.') + + for group_name in added_user_list: + self.logger.debug( + 'Executing: ' + '"deluser ' + str(self.username) + ' ' + group_name + '"') + self.execute('deluser ' + str(self.username) + ' ' + group_name) + + if len(deleted_user_list) != 0: + self.logger.debug('Adding user to groups that it has been removed in advance.') + + for group_name in deleted_user_list: + self.logger.debug( + 'Executing: ' + '"adduser ' + str(self.username) + ' ' + group_name + '"') + self.execute('adduser ' + str(self.username) + ' ' + group_name) + else: + self.logger.debug('Changes file not found for {} user.'.format(self.username)) + + +def handle_mode(context): + user_privilege = UserPrivilegeSafeMode(context) + user_privilege.handle_safe_mode() diff --git a/src/plugins/user-privilege/shutdown.py b/src/plugins/user-privilege/shutdown.py new file mode 100644 index 0000000..b734dcd --- /dev/null +++ b/src/plugins/user-privilege/shutdown.py @@ -0,0 +1,95 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +""" +Style Guide is PEP-8 +https://www.python.org/dev/peps/pep-0008/ +""" + +import glob +import json +import os + +from base.plugin.abstract_plugin import AbstractPlugin + + +class UserPrivilegeShutdownMode(AbstractPlugin): + def __init__(self, context): + super(AbstractPlugin, self).__init__() + self.context = context + self.logger = self.get_logger() + + def handle_for_this_user(self, username): + + p_path = self.Ahenk.plugins_path() + + privilege_file = p_path + 'user-privilege/privilege.changes/' + username + '.changes' + + if self.is_exist(privilege_file): + self.logger.debug('Reading privilege_file: ' + privilege_file) + with open(privilege_file) as data_file: + self.logger.debug('Creating object from JSON data file.') + data = json.load(data_file) + + command_path_list = data['command_path_list'] + added_user_list = data['added_user_list'] + deleted_user_list = data['deleted_user_list'] + + if len(command_path_list) != 0: + self.logger.debug('Removing wrapper files and renaming original files.') + + for command_path in command_path_list: + if os.path.exists(command_path + '-ahenk'): + self.logger.debug('Executing: ' + '"rm ' + command_path + '"') + self.execute('rm ' + command_path) + self.logger.debug( + 'Executing: ' + '"mv ' + command_path + '-ahenk ' + command_path + '"') + self.execute('mv ' + command_path + '-ahenk ' + command_path) + else: + self.logger.debug( + 'File will not be deleted because ' + command_path + 'does not exists.') + + if len(added_user_list) != 0: + self.logger.debug( + 'Removing user from groups that it has been added in advance.') + + for group_name in added_user_list: + self.logger.debug( + 'Executing: ' + '"deluser ' + str(username) + ' ' + group_name + '"') + self.execute('deluser ' + str(username) + ' ' + group_name) + + if len(deleted_user_list) != 0: + self.logger.debug('Adding user to groups that it has been removed in advance.') + + for group_name in deleted_user_list: + self.logger.debug( + 'Executing: ' + '"adduser ' + str(username) + ' ' + group_name + '"') + self.execute('adduser ' + str(username) + ' ' + group_name) + else: + self.logger.debug('Changes file not found for {} user.'.format(username)) + + def handle_shutdown_mode(self): + self.logger.debug('Handling shutdown mode.') + + changes_file_arr = self.Ahenk.plugins_path() + 'user-privilege/privilege.changes/*.changes' + change_files = glob.glob(changes_file_arr) + + if change_files is not None and len(change_files) > 0: + self.logger.debug('Some user changes found.') + for file in change_files: + tmp = file.replace(self.Ahenk.plugins_path() + 'user-privilege/privilege.changes/', '') + tmp = tmp.replace('.changes', '') + self.logger.debug('Handling init for user {0}'.format(tmp)) + try: + self.handle_for_this_user(tmp) + self.logger.debug('Handled init for user {0}'.format(tmp)) + except Exception as e: + self.logger.error( + 'A problem occurred while handling shutdown action for user {0}. Error Message: {1}'.format( + tmp, str(e))) + else: + self.logger.debug('Changes files not found.') + + +def handle_mode(context): + user_privilege = UserPrivilegeShutdownMode(context) + user_privilege.handle_shutdown_mode() diff --git a/usr/share/ahenk/__init__.py b/usr/share/ahenk/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/ahenkd.py b/usr/share/ahenk/ahenkd.py new file mode 100644 index 0000000..0e4e633 --- /dev/null +++ b/usr/share/ahenk/ahenkd.py @@ -0,0 +1,402 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +# Author: Volkan Şahin + +import os +import queue +import signal +import sys +import threading +import time +from glob import glob + +from base.agreement.agreement import Agreement +from base.command.command_manager import Commander +from base.command.command_runner import CommandRunner +from base.config.config_manager import ConfigManager +from base.database.ahenk_db_service import AhenkDbService +from base.deamon.base_daemon import BaseDaemon +from base.event.event_manager import EventManager +from base.execution.execution_manager import ExecutionManager +from base.logger.ahenk_logger import Logger +from base.messaging.message_response_queue import MessageResponseQueue +from base.messaging.messaging import Messaging +from base.messaging.messenger import Messenger +from base.plugin.plugin_manager_factory import PluginManagerFactory +from base.registration.registration import Registration +from base.scheduler.scheduler_factory import SchedulerFactory +from base.scope import Scope +from base.system.system import System +from base.task.task_manager import TaskManager +from base.util.util import Util +from easygui import msgbox + +sys.path.append('../..') + +ahenk_daemon = None + + +class AhenkDaemon(BaseDaemon): + """Ahenk service base class which initializes services and maintains events/commands""" + + @staticmethod + def reload(): + """ docstring""" + # reload service here + pass + + @staticmethod + def init_logger(): + """ docstring""" + logger = Logger() + logger.info('Log was set') + Scope.get_instance().set_logger(logger) + return logger + + @staticmethod + def init_config_manager(config_file_path, configfile_folder_path): + """ docstring""" + config_manager = ConfigManager(config_file_path, configfile_folder_path) + config = config_manager.read() + Scope.get_instance().set_configuration_manager(config) + return config + + @staticmethod + def init_scheduler(): + """ docstring""" + scheduler_ins = SchedulerFactory.get_intstance() + scheduler_ins.initialize() + Scope.get_instance().set_scheduler(scheduler_ins) + sc_thread = threading.Thread(target=scheduler_ins.run) + sc_thread.setDaemon(True) + sc_thread.start() + return scheduler_ins + + @staticmethod + def init_event_manager(): + """ docstring""" + event_manager = EventManager() + Scope.get_instance().set_event_manager(event_manager) + return event_manager + + @staticmethod + def init_ahenk_db(): + """ docstring""" + db_service = AhenkDbService() + db_service.connect() + db_service.initialize_table() + Scope.get_instance().set_sb_service(db_service) + return db_service + + @staticmethod + def init_messaging(): + """ docstring""" + message_manager = Messaging() + Scope.get_instance().set_message_manager(message_manager) + return message_manager + + @staticmethod + def init_plugin_manager(): + """ docstring""" + plugin_manager = PluginManagerFactory.get_instance() + Scope.get_instance().set_plugin_manager(plugin_manager) + # order changed, problem? + plugin_manager.load_plugins() + return plugin_manager + + @staticmethod + def init_task_manager(): + """ docstring""" + task_manager = TaskManager() + Scope.get_instance().set_task_manager(task_manager) + return task_manager + + @staticmethod + def init_registration(): + """ docstring""" + registration = Registration() + Scope.get_instance().set_registration(registration) + return registration + + @staticmethod + def init_execution_manager(): + """ docstring""" + execution_manager = ExecutionManager() + Scope.get_instance().set_execution_manager(execution_manager) + return execution_manager + + @staticmethod + def init_messenger(): + """ docstring""" + messenger_ = Messenger() + messenger_.connect_to_server() + Scope.get_instance().set_messenger(messenger_) + return messenger_ + + @staticmethod + def init_message_response_queue(): + """ docstring""" + response_queue = queue.Queue() + message_response_queue = MessageResponseQueue(response_queue) + message_response_queue.setDaemon(True) + message_response_queue.start() + Scope.get_instance().set_response_queue(response_queue) + return response_queue + + def check_registration(self): + """ docstring""" + # max_attempt_number = int(System.Hardware.Network.interface_size()) * 3 + max_attempt_number = 1 + # self.logger.debug() + # logger = Scope.getInstance().getLogger() + registration = Scope.get_instance().get_registration() + + try: + #if registration.is_registered() is False: + # self.logger.debug('Ahenk is not registered. Attempting for registration') + # if registration.registration_request() == False: + # self.registration_failed() + + if registration.is_registered() is False: + print("Registation attemp") + max_attempt_number -= 1 + self.logger.debug('Ahenk is not registered. Attempting for registration') + registration.registration_request(self.register_hostname,self.register_user_name,self.register_user_password) + + #if max_attempt_number < 0: + # self.logger.warning('Number of Attempting for registration is over') + # self.registration_failed() + # break + except Exception as e: + self.registration_failed() + self.logger.error('Registration failed. Error message: {0}'.format(str(e))) + + + def is_registered(self): + try: + registration = Scope.get_instance().get_registration() + if registration.is_registered() is False: + self.registration_failed() + + except Exception as e: + self.registration_failed() + self.logger.error('Registration failed. Error message: {0}'.format(str(e))) + + @staticmethod + def shutdown_mode(): + """ docstring""" + scope = Scope().get_instance() + plugin_manager = scope.get_plugin_manager() + plugin_manager.process_mode('shutdown') + + def registration_failed(self): + """ docstring""" + self.logger.error('Registration failed. All registration attempts were failed. Ahenk is stopping...') + print('Registration failed. Ahenk is stopping..') + ahenk_daemon.stop() + + @staticmethod + def reload_plugins(): + """ docstring""" + Scope.get_instance().get_plugin_manager().reloadPlugins() + + def reload_configuration(self): + # Not implemented yet + pass + + def reload_messaging(self): + # Not implemented yet + pass + + def reload_logger(self): + # Not implemented yet + pass + + def update_plugin_manager(self): + """ docstring""" + # TODO destroy plugin manager here + self.init_plugin_manager() + + def init_signal_listener(self): + """ docstring""" + try: + signal.signal(signal.SIGALRM, CommandRunner().run_command_from_fifo) + self.logger.info('Signal handler is set up') + except Exception as e: + self.logger.error('Signal handler could not set up. Error Message: {0} '.format(str(e))) + + @staticmethod + def init_pid_file(): + """ docstring""" + with open(System.Ahenk.pid_path(), 'w+') as f: + f.write(str(os.getpid())) + + @staticmethod + def init_fifo_file(): + """ docstring""" + if Util.is_exist(System.Ahenk.fifo_file()): + Util.delete_file(System.Ahenk.fifo_file()) + Util.create_file(System.Ahenk.fifo_file()) + Util.set_permission(System.Ahenk.fifo_file(), '600') + + def set_register_user(self, hostName, username, password): + self.register_hostname=hostName + self.register_user_name=username + self.register_user_password=password + + def disable_local_users(self): + + self.logger.info('Local users disable action start..') + conf_manager = Scope.get_instance().get_configuration_manager() + + if conf_manager.has_section('MACHINE'): + user_disabled = conf_manager.get("MACHINE", "user_disabled") + self.logger.info('User disabled value=' + str(user_disabled)) + if user_disabled == '0': + self.logger.info('local user disabling') + Scope.get_instance().get_registration().disable_local_users() + + conf_manager.set('MACHINE', 'user_disabled', '1') + + with open('/etc/ahenk/ahenk.conf', 'w') as configfile: + self.logger.info('oepning config file ') + conf_manager.write(configfile) + + user_disabled = conf_manager.get("MACHINE", "user_disabled") + self.logger.info('User succesfully disabled value=' + str(user_disabled)) + else: + self.logger.info('users already disabled') + + def run(self): + """ docstring""" + print('Ahenk running...') + + global_scope = Scope() + global_scope.set_instance(global_scope) + + config_file_folder_path = '/etc/ahenk/config.d/' + + # configuration manager must be first load + self.init_config_manager(System.Ahenk.config_path(), config_file_folder_path) + + # Logger must be second + self.logger = self.init_logger() + + self.init_pid_file() + self.logger.info('Pid file was created') + + self.init_fifo_file() + self.logger.info('Fifo file was created') + + self.init_event_manager() + self.logger.info('Event Manager was set') + + self.init_ahenk_db() + self.logger.info('DataBase Service was set') + + self.init_messaging() + self.logger.info('Message Manager was set') + + self.init_plugin_manager() + self.logger.info('Plugin Manager was set') + + self.init_scheduler() + self.logger.info('Scheduler was set') + + self.init_task_manager() + self.logger.info('Task Manager was set') + + self.init_registration() + self.logger.info('Registration was set') + + self.init_execution_manager() + self.logger.info('Execution Manager was set') + + self.check_registration() + + self.is_registered() + + self.disable_local_users() + + #self.logger.info('Ahenk was registered') + + self.messenger = self.init_messenger() + self.logger.info('Messenger was set') + + self.init_signal_listener() + self.logger.info('Signals listeners was set') + + # Agreement().agreement_contract_update() + + global_scope.put_custom_map('ahenk_daemon', ahenk_daemon) + self.init_message_response_queue() + + # if registration.is_ldap_registered() is False: + # logger.debug('Attempting to registering ldap') + # registration.ldap_registration_request() #TODO work on message + + self.logger.info('LDAP registration of Ahenk is completed') + + self.messenger.send_direct_message('test') + + while True: + time.sleep(1) + + +if __name__ == '__main__': + + ahenk_daemon = AhenkDaemon(System.Ahenk.pid_path()) + try: + if len(sys.argv) == 2 and (sys.argv[1] in ('start', 'stop', 'restart', 'status')): + ahenk_daemon.set_register_user(None, None, None) + if sys.argv[1] == 'start': + if System.Ahenk.is_running() is True: + print('There is already running Ahenk service. It will be killed.[{0}]'.format( + str(System.Ahenk.get_pid_number()))) + System.Process.kill_by_pid(int(System.Ahenk.get_pid_number())) + else: + print('Ahenk starting...') + ahenk_daemon.run() + elif sys.argv[1] == 'stop': + if System.Ahenk.is_running() is True: + raise SystemExit + else: + print('Ahenk not working!') + elif sys.argv[1] == 'restart': + if System.Ahenk.is_running() is True: + print('Ahenk restarting...') + ahenk_daemon.restart() + else: + print('Ahenk starting...') + ahenk_daemon.run() + elif sys.argv[1] == 'status': + print(Commander().status()) + else: + print('Unknown command. Usage : %s start|stop|restart|status|clean' % sys.argv[0]) + sys.exit(2) + elif len(sys.argv) > 2 and (sys.argv[1] in ('register')): + params = sys.argv[1] + hostName = sys.argv[2] + userName = sys.argv[3] + password = sys.argv[4] + ahenk_daemon.set_register_user(hostName,userName,password) + ahenk_daemon.run() + + else: + result = Commander().set_event(sys.argv) + if result is None: + print('Usage : {0} start|stop|restart|status|clean'.format(sys.argv[0])) + sys.exit(2) + elif result is True: + if System.Ahenk.is_running() is True: + os.kill(int(System.Ahenk.get_pid_number()), signal.SIGALRM) + except(KeyboardInterrupt, SystemExit): + if System.Ahenk.is_running() is True: + print('Ahenk stopping...') + result = Commander().set_event([None, 'stop']) + if result is True: + if System.Ahenk.is_running() is True: + os.kill(int(System.Ahenk.get_pid_number()), signal.SIGALRM) + else: + ahenk_daemon.stop() diff --git a/usr/share/ahenk/api/service/ps_util.py b/usr/share/ahenk/api/service/ps_util.py new file mode 100644 index 0000000..d0cdb58 --- /dev/null +++ b/usr/share/ahenk/api/service/ps_util.py @@ -0,0 +1,8 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +import psutil + +if __name__ == '__main__': + pass + diff --git a/usr/share/ahenk/base/__init__.py b/usr/share/ahenk/base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/agreement/agreement.py b/usr/share/ahenk/base/agreement/agreement.py new file mode 100644 index 0000000..367ff80 --- /dev/null +++ b/usr/share/ahenk/base/agreement/agreement.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +from base.scope import Scope +from base.util.util import Util +from base.system.system import System + + +class Agreement: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.message_manager = scope.get_message_manager() + self.messenger = scope.get_messenger() + self.db_service = scope.get_db_service() + self.ask_path = '/usr/share/ahenk/base/agreement/ask.py' + self.logger.debug('Instance initialized.') + + def agreement_contract_update(self): + self.messenger.send_direct_message(self.message_manager.agreement_request_msg()) + self.logger.debug('Requested updated agreement contract from lider.') + + def check_agreement(self, username): + self.logger.debug('Checking agreement for user {0}.'.format(username)) + contract_id = self.get_current_contract_id() + if contract_id is None: + self.logger.debug('There is no any contract in database.') + contract_id = '-1' + + if self.db_service.select_one_result('agreement', 'id', + " contract_id='{0}' and username='{1}' and choice='Y' " + .format(contract_id, username)) is not None: + self.logger.debug('{0} answered agreement..') + return True + elif self.db_service.select_one_result('agreement', 'id', + " contract_id='{0}' and username='{1}' and choice='N' ".format( + contract_id, username)) is not None: + return False + else: + return None + + def get_current_contract_id(self): + return self.db_service.select_one_result('contract', 'id', 'id =(select MAX(id) from contract)') + + def ask(self, username, display): + + result = self.db_service.select('contract', ['content', 'title', 'id'], 'id =(select MAX(id) from contract)') + + if result is None or len(result) < 1: + content = 'Ahenk kurulu bu bilgisayarda ilk defa oturum açıyorsunuz. ' \ + 'Devam ederseniz Lider-Ahenk in bilgisayar üzeride yapacağı ' \ + 'tüm işlemlere onay vermiş sayılacaksınız. Kabul ediyor musunuz?' \ + ' \n(Tanımlanmış zaman aralığında olumlu cevaplandırmadığınız takdirde oturumunuz ' \ + 'sonlandırılacaktır.)' + title = 'Ahenk Kurulu Bilgisayar Kullanım Anlaşması' + contract_id = '-1' + else: + content = str(result[0][0]) + title = result[0][1] + contract_id = result[0][2] + try: + agreement_path = System.Ahenk.received_dir_path() + Util.generate_uuid() + Util.write_file(agreement_path, content) + Util.set_permission(agreement_path, 777) + command = 'export DISPLAY={0};su - {1} -c \'python3 {2} \"$(cat {3})\" \"{4}\"\''.format(display, username, + self.ask_path, + agreement_path, + title) + result_code, p_out, p_err = Util.execute(command) + + pout = str(p_out).replace('\n', '') + if pout != 'Error': + if pout == 'Y': + self.logger.debug('Agreement was accepted by {0}.'.format(username)) + self.db_service.update('agreement', self.db_service.get_cols('agreement'), + [contract_id, username, Util.timestamp(), 'Y']) + elif pout == 'N': + self.db_service.update('agreement', self.db_service.get_cols('agreement'), + [contract_id, username, Util.timestamp(), 'N']) + self.logger.debug( + 'Agreement was ignored by {0}. Session will be closed'.format(username)) + else: + self.logger.error( + 'A problem occurred while executing ask.py. Error Message: {0}'.format(str(pout))) + Util.delete_file(agreement_path) + else: + self.logger.error( + 'A problem occurred while executing ask.py (Probably argument fault). Error Message: {0}'.format( + str(pout))) + + except Exception as e: + self.logger.error( + 'A Problem occurred while displaying agreement. Error Message: {0}'.format(str(e))) diff --git a/usr/share/ahenk/base/agreement/ahenkmessage.py b/usr/share/ahenk/base/agreement/ahenkmessage.py new file mode 100644 index 0000000..8fd07df --- /dev/null +++ b/usr/share/ahenk/base/agreement/ahenkmessage.py @@ -0,0 +1,47 @@ +import sys +from easygui import multpasswordbox, msgbox + +def ask(message, title, host): + + field_names=[] + + if host =='': + field_names.append("Etki Alanı Sunucusu:") + + field_names.append("Yetkili Kullanıcı") + field_names.append("Parola") + + field_values = multpasswordbox( + msg=message, + title=title, fields=(field_names)) + + if field_values is None: + return print('N'); + + is_fieldvalue_empty = False; + + for value in field_values: + if value == '': + is_fieldvalue_empty = True; + + if is_fieldvalue_empty: + msgbox("Lütfen zorunlu alanları giriniz.", ok_button="Tamam") + return print('Z'); + + if host =='': + print(field_values[0],field_values[1],field_values[2]) + else: + print(field_values[0], field_values[1]) + +if __name__ == '__main__': + + if len(sys.argv) > 1: + try: + message=sys.argv[1] + title=sys.argv[2] + host=sys.argv[3] + ask(message,title, host) + except Exception as e: + print(str(e)) + else: + print('Argument fault. Check your parameters or content of parameters. Parameters: ' + str(sys.argv)) \ No newline at end of file diff --git a/usr/share/ahenk/base/agreement/ask.py b/usr/share/ahenk/base/agreement/ask.py new file mode 100644 index 0000000..d584d5a --- /dev/null +++ b/usr/share/ahenk/base/agreement/ask.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import sys +import easygui + + +def ask(content, title): + choice = easygui.buttonbox(msg=title, title=content, choices=['TAMAM']) + if choice: + print('Y') + else: + print('N') + + +if __name__ == '__main__': + + if len(sys.argv) == 3: + try: + ask(sys.argv[1], sys.argv[2]) + except Exception as e: + print(str(e)) + else: + print('Argument fault. Check your parameters or content of parameters. Parameters: ' + str(sys.argv)) diff --git a/usr/share/ahenk/base/agreement/confirm.py b/usr/share/ahenk/base/agreement/confirm.py new file mode 100644 index 0000000..12d8847 --- /dev/null +++ b/usr/share/ahenk/base/agreement/confirm.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import sys +import easygui + + +def confirm(message, title): + choice = easygui.buttonbox(msg=message, title=title, choices=["Tamam"]) + + if choice: + print('Y') + else: + print('N') + + +if __name__ == '__main__': + + if len(sys.argv) == 3: + try: + confirm(sys.argv[1], sys.argv[2]) + except Exception as e: + print(str(e)) + else: + print('Argument fault. Check your parameters or content of parameters. Parameters: ' + str(sys.argv)) diff --git a/usr/share/ahenk/base/agreement/unregistrationmessage.py b/usr/share/ahenk/base/agreement/unregistrationmessage.py new file mode 100644 index 0000000..53d153b --- /dev/null +++ b/usr/share/ahenk/base/agreement/unregistrationmessage.py @@ -0,0 +1,39 @@ +import sys +from easygui import multpasswordbox, msgbox + +def ask(message, title): + + field_names = [] + field_names.append("Yetkili Kullanıcı") + field_names.append("Parola") + + field_values = multpasswordbox( + msg=message, + title=title, fields=(field_names)) + + if field_values is None: + return print('N'); + + is_fieldvalue_empty = False; + + for value in field_values: + if value == '': + is_fieldvalue_empty = True; + + if is_fieldvalue_empty: + msgbox("Lütfen zorunlu alanları giriniz.", ok_button="Tamam") + return print('Z'); + + print(field_values[0], field_values[1]) + +if __name__ == '__main__': + + if len(sys.argv) > 1: + try: + message=sys.argv[1] + title=sys.argv[2] + ask(message,title) + except Exception as e: + print(str(e)) + else: + print('Argument fault. Check your parameters or content of parameters. Parameters: ' + str(sys.argv)) \ No newline at end of file diff --git a/usr/share/ahenk/base/command/command_manager.py b/usr/share/ahenk/base/command/command_manager.py new file mode 100644 index 0000000..cad7d2e --- /dev/null +++ b/usr/share/ahenk/base/command/command_manager.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + + +import configparser +import datetime +import json +import os +import queue as Queue +import threading + +from base.command.fifo import Fifo +from base.model.enum.content_type import ContentType +from base.model.enum.message_code import MessageCode +from base.model.enum.message_type import MessageType +from base.system.system import System +from base.util.util import Util + + +class Commander(object): + def __init__(self): + pass + + def set_event(self, *args): + + if args is None or len(args) < 1: + print('Lack of arguments') + + params = args[0] + data = dict() + + if System.Ahenk.is_running() is True: + + if len(params) > 1 and params[1] == 'clean': + print('Ahenk stopping') + System.Process.kill_by_pid(int(System.Ahenk.get_pid_number())) + self.clean() + return False + + elif len(params) > 4 and params[1] == 'login': + print('{0} logging in'.format(str(params[2]))) + data['event'] = params[1] + data['username'] = params[2] + data['desktop'] = params[3] + data['display'] = params[4] + if len(params) == 6: + data['ip'] = params[5] + + elif len(params) == 3 and params[1] == 'logout': + print('{0} logging out'.format(str(params[2]))) + data['event'] = params[1] + data['username'] = params[2] + + elif len(params) == 4 and params[1] == 'logout': + print('{0} logging out'.format(str(params[2]))) + data['event'] = params[1] + data['username'] = params[2] + data['ip'] = params[3] + + elif len(params) == 2 and params[1] == 'stop': + data['event'] = 'stop' + + elif len(params) == 4 and params[1] == 'load' and params[2] == '-p': + data['event'] = 'load' + data['plugins'] = params[3] + + elif len(params) == 4 and params[1] == 'reload' and params[2] == '-p': + data['event'] = 'reload' + data['plugins'] = params[3] + + elif len(params) == 4 and params[1] == 'remove' and params[2] == '-p': + data['event'] = 'remove' + data['plugins'] = params[3] + + elif len(params) > 1 and params[1] == 'unregister': + data['event'] = params[1] + + + elif len(params) > 5 and params[1] == 'send': + data['event'] = params[1] + response = dict() + response['timestamp'] = str(datetime.datetime.now().strftime("%d-%m-%Y %I:%M")) + response['responseMessage'] = 'This content was sent via ahenk terminal command' + + if params[2] == '-t': + response['responseCode'] = MessageCode.TASK_PROCESSED.value + response['type'] = MessageType.TASK_STATUS.value + response['taskId'] = params[3] + if params[4] == '-m': + response['contentType'] = ContentType.APPLICATION_JSON.value + response['responseData'] = params[5] + elif params[4] == '-f': + if os.path.exists(str(params[5])): + response['contentType'] = self.get_relevant_type(str(params[5])) + response['responseData'] = Util.read_file(str(params[5]), 'rb') + else: + print( + 'Wrong or missing parameter. Usage: send -t -m|-f |') + return None + + if len(params) > 6: + if params[6] == '-e': + response['responseCode'] = MessageCode.TASK_ERROR.value + elif params[6] == '-w': + response['responseCode'] = MessageCode.TASK_WARNING.value + elif params[6] == '-s': + response['responseCode'] = MessageCode.TASK_PROCESSED.value + else: + print( + 'Wrong or missing parameter.(-e|-s|-w parameters are optional) Usage: send -t -m|-f | -e|-s|-w') + return None + + elif len(params) > 7 and params[2] == '-p': + response['responseCode'] = MessageCode.POLICY_PROCESSED.value + response['type'] = MessageType.POLICY_STATUS.value + response['policyVersion'] = params[3] + + if params[4] == '-c': + response['commandExecutionId'] = params[5] + + if params[6] == '-m': + response['contentType'] = ContentType.APPLICATION_JSON.value + response['responseData'] = params[7] + elif params[6] == '-f': + if os.path.exists(str(params[7])): + response['contentType'] = self.get_relevant_type(str(params[7])) + response['responseData'] = Util.read_file(str(params[7]), 'rb') + else: + print( + 'Wrong or missing parameter. Usage: send -p -c -m|-f |') + return None + + if len(params) > 8: + if params[8] == '-e': + response['responseCode'] = MessageCode.POLICY_ERROR.value + elif params[8] == '-w': + response['responseCode'] = MessageCode.POLICY_WARNING.value + elif params[8] == '-s': + response['responseCode'] = MessageCode.POLICY_PROCESSED.value + else: + print( + 'Wrong or missing parameter.(-e|-s|-w parameters are optional) Usage: send -p -c -m|-f | -e|-s|-w') + return None + else: + print( + 'Wrong or missing parameter. Usage: send -p -c -m|-f | -e|-s|-w') + return None + + resp = str(response).replace("\"{", "{") + resp = resp.replace("}\"", "}") + resp = resp.replace("'", "\"") + data['message'] = json.loads(resp) + # data['message'] = ast.literal_eval(str(response)) + + else: + print('Wrong or missing parameter. Usage : %s start|stop|restart|status|clean|send') + return None + else: + + if params[1] == 'clean': + self.clean() + + else: + print('Ahenk not running!') + return None + + if len(data) > 0: + fifo = Fifo() + thread = threading.Thread(target=fifo.push(str(json.dumps(data)) + '\n')) + thread.start() + + return True + + def get_relevant_type(self, extension): + + extension = extension.lower() + if extension == 'json': + return ContentType.APPLICATION_JSON + elif extension == 'txt': + return ContentType.TEXT_PLAIN + elif extension == 'dec': + return ContentType.APPLICATION_MS_WORD + elif extension == 'pdf': + return ContentType.APPLICATION_PDF + elif extension == 'xls': + return ContentType.APPLICATION_VND_MS_EXCEL + elif extension == 'jpeg' or extension == 'jpg': + return ContentType.IMAGE_JPEG + elif extension == 'png': + return ContentType.IMAGE_PNG + elif extension == 'html' or extension == 'htm': + return ContentType.TEXT_HTML + else: + return ContentType.TEXT_PLAIN + + def get_event(self): + fifo = Fifo() + queue = Queue.Queue() + thread = threading.Thread(target=fifo.pull(queue)) + thread.start() + thread.join() + result = queue.get() + if result is not None: + return result + else: + return None + + def clean(self): + print('Ahenk cleaning..') + try: + config = configparser.ConfigParser() + config._interpolation = configparser.ExtendedInterpolation() + config.read(System.Ahenk.config_path()) + db_path = config.get('BASE', 'dbPath') + + if Util.is_exist(System.Ahenk.fifo_file()): + Util.delete_file(System.Ahenk.fifo_file()) + + if Util.is_exist(db_path): + Util.delete_file(db_path) + + if Util.is_exist(System.Ahenk.pid_path()): + Util.delete_file(System.Ahenk.pid_path()) + + config.set('CONNECTION', 'uid', '') + config.set('CONNECTION', 'password', '') + + with open(System.Ahenk.config_path(), 'w') as file: + config.write(file) + file.close() + print('Ahenk cleaned.') + except Exception as e: + print('Error while running clean command. Error Message {0}'.format(str(e))) + + def status(self): + ahenk_state = False + + if System.Ahenk.is_running() is True: + ahenk_state = True + return "Ahenk Active:{0}\nInstalled Plugins:{1}".format(ahenk_state, str(System.Ahenk.installed_plugins())) + + def force_clean(self): + # TODO + pass diff --git a/usr/share/ahenk/base/command/command_runner.py b/usr/share/ahenk/base/command/command_runner.py new file mode 100644 index 0000000..3bb804c --- /dev/null +++ b/usr/share/ahenk/base/command/command_runner.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import json +import time +from multiprocessing import Process + +from base.agreement.agreement import Agreement +from base.command.command_manager import Commander +from base.scope import Scope +from base.system.system import System +from base.timer.setup_timer import SetupTimer +from base.timer.timer import Timer +from base.util.util import Util + + +class CommandRunner(object): + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.plugin_manager = scope.get_plugin_manager() + self.message_manager = scope.get_message_manager() + self.messenger = scope.get_messenger() + self.conf_manager = scope.get_configuration_manager() + self.db_service = scope.get_db_service() + self.execute_manager = scope.get_execution_manager() + + def check_last_login(self): + last_login_tmstmp = self.db_service.select_one_result('session', 'timestamp') + if not last_login_tmstmp: + return True + + if (int(time.time()) - int(last_login_tmstmp)) < 10: + return False + else: + return True + + def delete_polkit_user(self): + content = "[Configuration] \nAdminIdentities=unix-user:root" + ahenk_policy_file = "/etc/polkit-1/localauthority.conf.d/99-ahenk-policy.conf" + if not Util.is_exist(ahenk_policy_file): + self.logger.info('Ahenk polkit file not found') + else: + Util.delete_file(ahenk_policy_file) + Util.write_file(ahenk_policy_file, content) + self.logger.info('Root added ahenk polkit file') + + def run_command_from_fifo(self, num, stack): + """ docstring""" + + while True: + try: + event = Commander().get_event() + if event is None: + break + json_data = json.loads(event) + except Exception as e: + self.logger.error( + 'A problem occurred while loading json. Check json format! Error Message: {0}.' + ' Event = {1}'.format(str(e), str(event))) + return + + if json_data is not None: + + self.logger.debug('Signal handled') + self.logger.debug('Signal is :{0}'.format(str(json_data['event']))) + + if str(json_data['event']) == 'login' and self.check_last_login(): + username = json_data['username'] + display = json_data['display'] + desktop = json_data['desktop'] + + + ip = None + if 'ip' in json_data: + ip = json_data['ip'] + + self.logger.info('login event is handled for user: {0}'.format(username)) + login_message = self.message_manager.login_msg(username,ip) + self.messenger.send_direct_message(login_message) + + agreement = Agreement() + agreement_choice = None + + if agreement.check_agreement(username) is not True and System.Ahenk.agreement() == '1': + self.logger.debug('User {0} has not accepted agreement.'.format(username)) + thread_ask = Process(target=agreement.ask, args=(username, display,)) + thread_ask.start() + + agreement_timeout = self.conf_manager.get('SESSION', 'agreement_timeout') + + timeout = int(agreement_timeout) # sec + timer = time.time() + while 1: + if thread_ask.is_alive() is False: + self.logger.warning('{0} was answered the question '.format(username)) + if Agreement().check_agreement(username) is True: + self.logger.warning('Choice of {0} is YES'.format(username)) + agreement_choice = True + break + elif Agreement().check_agreement(username) is False: + self.logger.warning('Choice of {0} is NO'.format(username)) + agreement_choice = False + Util.close_session(username) + break + + if (time.time() - timer) > timeout: + if thread_ask.is_alive(): + thread_ask.terminate() + Util.close_session(username) + self.logger.warning( + 'Session of {0} was ended because of timeout of contract agreement'.format( + username)) + break + time.sleep(1) + + if agreement_choice is not None: + self.messenger.send_direct_message( + self.message_manager.agreement_answer_msg(username, agreement_choice)) + else: + agreement_choice = True + + if agreement_choice is True or System.Ahenk.agreement() != '1': + self.db_service.delete('session', 'username=\'{0}\''.format(username)) + + self.logger.info( + 'Display is {0}, desktop env is {1} for {2}'.format(display, desktop, + username)) + session_columns = self.db_service.get_cols('session') + self.db_service.update('session', session_columns, + [username, display, desktop, str(int(time.time())), ip]) + get_policy_message = self.message_manager.policy_request_msg(username) + + self.plugin_manager.process_mode('safe', username) + self.plugin_manager.process_mode('login', username) + + kward = dict() + kward['timeout_args'] = username + kward['checker_args'] = username + + SetupTimer.start(Timer(timeout=System.Ahenk.get_policy_timeout(), + timeout_function=self.execute_manager.execute_default_policy, + checker_func=self.execute_manager.is_policy_executed, kwargs=kward)) + + self.logger.info( + 'Requesting updated policies from Lider. If Ahenk could not reach updated ' + 'policies in {0} sec, booked policies will be executed'.format( + System.Ahenk.get_policy_timeout())) + self.messenger.send_direct_message(get_policy_message) + + elif str(json_data['event']) == 'logout': + username = json_data['username'] + self.db_service.delete('session', 'username=\'{0}\''.format(username)) + self.execute_manager.remove_user_executed_policy_dict(username) + # TODO delete all user records while initializing + self.logger.info('logout event is handled for user: {0}'.format(username)) + ip = None + if 'ip' in json_data: + ip = json_data['ip'] + logout_message = self.message_manager.logout_msg(username,ip) + self.messenger.send_direct_message(logout_message) + + self.logger.info('Ahenk polkit file deleting..') + self.delete_polkit_user(); + + self.plugin_manager.process_mode('logout', username) + self.plugin_manager.process_mode('safe', username) + + elif str(json_data['event']) == 'send': + self.logger.info('Sending message over ahenkd command. Response Message: {0}'.format( + json.dumps(json_data['message']))) + message = json.dumps(json_data['message']) + self.messenger.send_direct_message(message) + + + elif str(json_data['event']) == 'unregister': + self.logger.info('Unregistering..') + unregister_message = self.message_manager.unregister_msg() + if unregister_message is not None: + self.messenger.send_direct_message(unregister_message) + + elif str(json_data['event']) == 'load': + plugin_name = str(json_data['plugins']) + + if plugin_name == 'all': + self.logger.debug('All plugins are loading to ahenk') + self.plugin_manager.load_plugins() + else: + for p_name in plugin_name.split(','): + self.logger.debug('{0} plugin is loading to ahenk'.format(p_name)) + self.plugin_manager.load_single_plugin(p_name) + + elif str(json_data['event']) == 'reload': + plugin_name = str(json_data['plugins']) + + if plugin_name == 'all': + self.logger.debug('All plugins are reloading to ahenk') + self.plugin_manager.reload_plugins() + else: + for p_name in plugin_name.split(','): + self.logger.debug('{0} plugin is reloading to ahenk'.format(p_name)) + self.plugin_manager.reload_single_plugin(p_name) + + elif str(json_data['event']) == 'remove': + plugin_name = str(json_data['plugins']) + + if plugin_name == 'all': + self.logger.debug('All plugins are removing from ahenk') + self.plugin_manager.remove_plugins() + else: + for p_name in plugin_name.split(','): + self.logger.debug('{0} plugin is removing from ahenk'.format(p_name)) + self.plugin_manager.remove_single_plugin(p_name) + + elif str(json_data['event']) == 'stop': + self.plugin_manager.process_mode('shutdown') + self.logger.info('Shutdown mode activated.') + + # TODO timeout + while self.running_plugin() is False: + self.logger.debug('Waiting for progress of plugins...') + time.sleep(0.5) + + Util.delete_file(System.Ahenk.fifo_file()) + Scope().get_instance().get_custom_param('ahenk_daemon').stop() + else: + self.logger.error('Unknown command error. Command:' + json_data['event']) + self.logger.debug('Processing of handled event is completed') + + def running_plugin(self): + """ docstring""" + for plugin in self.plugin_manager.plugins: + if plugin.keep_run is True: + return False + return True diff --git a/usr/share/ahenk/base/command/fifo.py b/usr/share/ahenk/base/command/fifo.py new file mode 100644 index 0000000..6ab6433 --- /dev/null +++ b/usr/share/ahenk/base/command/fifo.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import threading + + +class Fifo(object): + def __init__(self): + self.lock = threading.Lock() + self.path = '/tmp/liderahenk.fifo' + + def push(self, content): + file = None + self.lock.acquire() + try: + file = open(self.path, 'a+') + file.write(content) + except Exception as e: + print('Error:{0}'.format(str(e))) + finally: + file.close() + self.lock.release() + + def pull(self, queue): + result = None + self.lock.acquire() + try: + lines = open(self.path, 'rb').readlines() + if lines is not None and len(lines) > 0: + result = lines[0].decode("unicode_escape") + w_file = open(self.path, 'wb') + w_file.writelines(lines[1:]) + w_file.close() + except Exception as e: + print('Error:{0}'.format(str(e))) + finally: + self.lock.release() + queue.put(result) diff --git a/usr/share/ahenk/base/config/__init__.py b/usr/share/ahenk/base/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/config/config_manager.py b/usr/share/ahenk/base/config/config_manager.py new file mode 100644 index 0000000..0b0da25 --- /dev/null +++ b/usr/share/ahenk/base/config/config_manager.py @@ -0,0 +1,41 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN + +import os +from configparser import SafeConfigParser +from os import listdir +from os.path import isfile, join + + +class ConfigManager(object): + """ + This class written for configuration file management of ahenk and ahenk plugins + Sample ahenk configuration file path /etc/ahenk/ahenk.conf and sample ahenk plugins configuration folder path /etc/ahenk/config.d/ + Usage: Takes two argument, - both of them are optional - one of the is a configuration file path the other one + is configuration files folder path + """ + + def __init__(self, configuration_file_path=None, configuration_folder_path=None): + self.configurationFilePath = configuration_file_path + self.configurationFolderPath = configuration_folder_path + + def read(self): + config_files = [] + + # Check if given ahenk configuration file exists + # If file exists add it to configFiles array. + # TODO must write config file validater !! + if self.configurationFilePath: + if os.path.exists(self.configurationFilePath): + config_files.append(self.configurationFilePath) + + if self.configurationFolderPath and os.path.exists(self.configurationFolderPath): + files = [f for f in listdir(self.configurationFolderPath) if isfile(join(self.configurationFolderPath, f))] + for f in files: + config_files.append(join(self.configurationFolderPath, f)) + + parser = SafeConfigParser() + configValues = parser.read(config_files) + + return parser diff --git a/usr/share/ahenk/base/database/ahenk_db_service.py b/usr/share/ahenk/base/database/ahenk_db_service.py new file mode 100644 index 0000000..c3a78fc --- /dev/null +++ b/usr/share/ahenk/base/database/ahenk_db_service.py @@ -0,0 +1,196 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +# Author: Volkan Şahin +import sqlite3 +import threading +from base.scope import Scope + + +class AhenkDbService(object): + """ + Sqlite manager for ahenk + """ + + def __init__(self): + scope = Scope.get_instance() + self.logger = scope.get_logger() + self.configurationManager = scope.get_configuration_manager() + self.db_path = self.configurationManager.get('BASE', 'dbPath') + self.connection = None + self.cursor = None + + self.lock = threading.Lock() + + # TODO get columns anywhere + # TODO scheduler db init get here + + def initialize_table(self): + + self.check_and_create_table('task', + ['id INTEGER', 'create_date TEXT', 'modify_date TEXT', 'command_cls_id TEXT', + 'parameter_map BLOB', 'deleted INTEGER', 'plugin TEXT', 'cron_expr TEXT', + 'file_server TEXT']) + self.check_and_create_table('policy', + ['id INTEGER PRIMARY KEY AUTOINCREMENT', 'type TEXT', 'version TEXT', 'name TEXT', + 'execution_id TEXT','expiration_date TEXT']) + self.check_and_create_table('profile', ['id INTEGER', 'create_date TEXT', 'label TEXT', 'description TEXT', + 'overridable INTEGER', 'active TEXT', 'deleted TEXT', + 'profile_data TEXT', 'modify_date TEXT', 'plugin TEXT']) + self.check_and_create_table('plugin', + ['id INTEGER PRIMARY KEY AUTOINCREMENT', 'active TEXT', 'create_date TEXT', + 'deleted TEXT', 'description TEXT', 'machine_oriented TEXT', 'modify_date TEXT', + 'name TEXT', 'policy_plugin TEXT', 'user_oriented TEXT', 'version TEXT', + 'task_plugin TEXT', 'x_based TEXT']) + self.check_and_create_table('registration', + ['jid TEXT', 'password TEXT', 'registered INTEGER', 'dn TEXT', 'params TEXT', + 'timestamp TEXT']) + self.check_and_create_table('contract', ['id INTEGER PRIMARY KEY AUTOINCREMENT', 'content BLOB', 'title TEXT', + 'timestamp TEXT']) + self.check_and_create_table('agreement', + ['id INTEGER PRIMARY KEY AUTOINCREMENT', 'contract_id TEXT', 'username TEXT', + 'timestamp TEXT', 'choice TEXT']) + self.check_and_create_table('session', ['id INTEGER PRIMARY KEY AUTOINCREMENT','username TEXT', 'display TEXT', 'desktop TEXT', 'timestamp TEXT', 'ip TEXT']) + + self.check_and_create_table('mail', ['id INTEGER PRIMARY KEY AUTOINCREMENT', 'command TEXT', 'mailstatus INTEGER', 'timestamp TEXT']) + + self.check_and_create_table('service', ['id INTEGER PRIMARY KEY AUTOINCREMENT', 'serviceName TEXT', 'serviceStatus TEXT','timestamp TEXT','task_id INTEGER']) + + + def get_cols(self, table_name): + if table_name == 'agreement': + return ['contract_id', 'username', 'timestamp', 'choice'] + elif table_name == 'contract': + return ['content', 'title', 'timestamp'] + elif table_name == 'session': + return ['username', 'display', 'desktop', 'timestamp', 'ip'] + elif table_name == 'task': + return ['id', 'create_date', 'modify_date', 'command_cls_id', 'parameter_map', 'deleted', 'plugin', + 'cron_expr', 'file_server'] + elif table_name == 'plugin': + return ['id', 'active', 'create_date', 'deleted', 'description', 'machine_oriented', 'modify_date', 'name', + 'policy_plugin', 'user_oriented', 'version', 'task_plugin', 'x_based'] + else: + return None + + def connect(self): + try: + self.connection = sqlite3.connect(self.db_path, check_same_thread=False) + self.cursor = self.connection.cursor() + except Exception as e: + self.logger.error('Database connection error: {0}'.format(str(e))) + + def check_and_create_table(self, table_name, cols): + + try: + self.lock.acquire(True) + if self.cursor: + cols = ', '.join([str(x) for x in cols]) + self.cursor.execute('create table if not exists ' + table_name + ' (' + cols + ')') + else: + self.logger.warning('Could not create table cursor is None! Table Name : {0}'.format(str(table_name))) + finally: + self.lock.release() + + def drop_table(self, table_name): + try: + self.lock.acquire(True) + sql = 'DROP TABLE ' + table_name + self.cursor.execute(sql) + self.connection.commit() + finally: + self.lock.release() + + def update(self, table_name, cols, args, criteria=None): + try: + self.lock.acquire(True) + if self.connection: + if criteria is None: + cols = ', '.join([str(x) for x in cols]) + params = ', '.join(['?' for x in args]) + sql = 'INSERT INTO ' + table_name + ' (' + cols + ') VALUES (' + params + ')' + else: + update_list = '' + for index in range(len(cols)): + update_list = update_list + ' ' + cols[index] + ' = ?,' + update_list = update_list.strip(',') + sql = 'UPDATE ' + table_name + ' SET ' + update_list + ' where ' + criteria + self.cursor.execute(sql, tuple(args)) + self.connection.commit() + return self.cursor.lastrowid + else: + self.logger.warning('Could not update table cursor is None! Table Name : {0}'.format(str(table_name))) + return None + except Exception as e: + self.logger.error( + 'Updating table error ! Table Name : {0} Error Mesage: {1}'.format(str(table_name), str(e))) + finally: + self.lock.release() + + def delete(self, table_name, criteria): + try: + self.lock.acquire(True) + if self.cursor: + sql = 'DELETE FROM ' + table_name + if criteria: + sql += ' where ' + str(criteria) + self.cursor.execute(sql) + self.connection.commit() + finally: + self.lock.release() + + def findByProperty(self): + # Not implemented yet + pass + + def select(self, table_name, cols='*', criteria='', orderby=''): + if self.cursor: + try: + self.lock.acquire(True) + if not cols == '*': + cols = ', '.join([str(x) for x in cols]) + sql = 'SELECT ' + cols + ' FROM ' + table_name + if criteria != '': + sql += ' where ' + sql += criteria + if orderby != '': + sql += ' order by ' + sql += orderby + + self.cursor.execute(sql) + rows = self.cursor.fetchall() + return rows + except: + raise + finally: + self.lock.release() + else: + self.logger.warning('Could not select table cursor is None! Table Name : {0}'.format(str(table_name))) + + def select_one_result(self, table_name, col, criteria=''): + if self.cursor: + try: + self.lock.acquire(True) + sql = 'SELECT ' + col + ' FROM ' + table_name + if criteria != '': + sql += ' where ' + sql += criteria + self.cursor.execute(sql) + row = self.cursor.fetchone() + if row is not None: + return row[0] + else: + return None + except: + raise + finally: + self.lock.release() + else: + self.logger.warning('Could not select table cursor is None! Table Name : {0}'.format(str(table_name))) + + def close(self): + try: + self.cursor.close() + self.connection.close() + except Exception as e: + self.logger.error('Closing database connection error: {0}'.format(str(e))) \ No newline at end of file diff --git a/usr/share/ahenk/base/deamon/__init__.py b/usr/share/ahenk/base/deamon/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/deamon/base_daemon.py b/usr/share/ahenk/base/deamon/base_daemon.py new file mode 100644 index 0000000..8d46139 --- /dev/null +++ b/usr/share/ahenk/base/deamon/base_daemon.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +import atexit +import os +import sys +import time +from signal import SIGTERM + + +class BaseDaemon(object): + """ + A generic daemon class. + + Usage: subclass the Daemon class and override the run() method + """ + + startmsg = 'started with pid %s' + + def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + self.pidfile = pidfile + + def daemonize(self): + """ + do the UNIX double-fork magic, see Stevens' "Advanced + Programming in the UNIX Environment" for details (ISBN 0201563177) + http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 + """ + try: + pid = os.fork() + if pid > 0: + # exit first parent + sys.exit(0) + except OSError as e: + sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.exit(1) + + # decouple from parent environment + os.chdir('.') + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent + sys.exit(0) + except OSError as e: + sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.exit(1) + + # redirect standard file descriptors + si = open(self.stdin, 'r') + so = open(self.stdout, 'a+') + se = open(self.stderr, 'a+') + + pid = str(os.getpid()) + + sys.stderr.write("\n%s\n" % self.startmsg % pid) + sys.stderr.flush() + + if self.pidfile: + open(self.pidfile, 'w+').write("%s\n" % pid) + + atexit.register(self.delpid) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + + def delpid(self): + os.remove(self.pidfile) + + def start(self): + """ + Start the daemon + """ + # Check for a pidfile to see if the daemon already runs + try: + pf = open(self.pidfile, 'r') + pid = int(pf.read().strip()) + pf.close() + except IOError: + pid = None + + if pid: + message = "pidfile %s already exist. Daemon already running?\n" + sys.stderr.write(message % self.pidfile) + sys.exit(1) + + # Start the daemon + self.daemonize() + self.run() + + def stop(self): + """ + Stop the daemon + """ + # Get the pid from the pidfile + try: + pf = open(self.pidfile, 'r') + pid = int(pf.read().strip()) + pf.close() + except IOError: + pid = None + + if not pid: + message = "pidfile %s does not exist. Daemon not running?\n" + sys.stderr.write(message % self.pidfile) + return # not an error in a restart + + # Try killing the daemon process + try: + while 1: + os.kill(pid, SIGTERM) + time.sleep(0.1) + except OSError as err: + err = str(err) + if err.find('No such process') > 0: + if os.path.exists(self.pidfile): + os.remove(self.pidfile) + else: + print(str(err)) + sys.exit(1) + + def restart(self): + """ + Restart the daemon + """ + self.stop() + self.start() + + def run(self): + """ + You should override this method when you subclass Daemon. It will be called after the process has been + daemonized by start() or restart(). + """ diff --git a/usr/share/ahenk/base/event/event_base.py b/usr/share/ahenk/base/event/event_base.py new file mode 100644 index 0000000..7c23cb1 --- /dev/null +++ b/usr/share/ahenk/base/event/event_base.py @@ -0,0 +1,39 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# @author: İsmail BAŞARAN + + +class EventBase: + """ + This is base event class for event management. + """ + + listeners = [] + + def __init__(self): + self.listeners.append(self) + self.listener_events = [] + + def register_event(self, event_name, callback_func): + """ + Registers event listener. + Args: + event_name : name of event, user specify event name + callback_func : when an event fire with specified event name this method will call + """ + self.listener_events.append({'event_name': event_name, 'callback_func': callback_func}) + + +class Event: + """ + This is event class. Takes two argument ; + Args: + event_name : name of event. + callback_args : arguments specified by user. This function will transmit args to callback function directly. + """ + + def __init__(self, event_name, *callback_args): + for listener in EventBase.listeners: + for listener_cls in listener.listener_events: + if listener_cls['event_name'] == event_name: + listener_cls['callback_func'](*callback_args) diff --git a/usr/share/ahenk/base/event/event_manager.py b/usr/share/ahenk/base/event/event_manager.py new file mode 100644 index 0000000..c81819b --- /dev/null +++ b/usr/share/ahenk/base/event/event_manager.py @@ -0,0 +1,15 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# @author: İsmail BAŞARAN + +from base.event.event_base import EventBase, Event + + +class EventManager(EventBase): + """docstring for EventManager""" + + def __init__(self): + EventBase.__init__(self) + + def fireEvent(self, event_name, *args): + Event(event_name, *args) diff --git a/usr/share/ahenk/base/execution/__init__.py b/usr/share/ahenk/base/execution/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/execution/execution_manager.py b/usr/share/ahenk/base/execution/execution_manager.py new file mode 100644 index 0000000..dc699e9 --- /dev/null +++ b/usr/share/ahenk/base/execution/execution_manager.py @@ -0,0 +1,554 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import json +import time +from base.file.file_transfer_manager import FileTransferManager +from base.model.enum.content_type import ContentType +from base.model.enum.message_code import MessageCode +from base.model.enum.message_type import MessageType +from base.model.plugin_bean import PluginBean +from base.model.policy_bean import PolicyBean +from base.model.profile_bean import ProfileBean +from base.model.response import Response +from base.model.task_bean import TaskBean +from base.scheduler.custom.schedule_job import ScheduleTaskJob +from base.scope import Scope +from base.system.system import System +from base.util.util import Util +from easygui import * + + +class ExecutionManager(object): + """docstring for FileTransferManager""" + + # TODO more logs + def __init__(self): + super(ExecutionManager, self).__init__() + + scope = Scope.get_instance() + self.config_manager = scope.get_configuration_manager() + self.event_manager = scope.get_event_manager() + self.task_manager = scope.get_task_manager() + self.messenger = scope.get_messenger() + self.logger = scope.get_logger() + self.db_service = scope.get_db_service() + self.message_manager = scope.get_message_manager() + self.plugin_manager = scope.get_plugin_manager() + self.policy_executed = dict() + + self.event_manager.register_event(MessageType.EXECUTE_SCRIPT.value, self.execute_script) + self.event_manager.register_event(MessageType.EXECUTE_TASK.value, self.execute_task) + self.event_manager.register_event(MessageType.EXECUTE_POLICY.value, self.execute_policy) + self.event_manager.register_event(MessageType.INSTALL_PLUGIN.value, self.install_plugin) + self.event_manager.register_event(MessageType.RESPONSE_AGREEMENT.value, self.agreement_update) + self.event_manager.register_event(MessageType.UPDATE_SCHEDULED_TASK.value, self.update_scheduled_task) + self.event_manager.register_event(MessageType.REGISTRATION_RESPONSE.value, self.unregister) # registration message for unregister event + self.event_manager.register_event(MessageType.LOGIN_RESPONSE.value, self.login_response) # registration message for unregister event + + def agreement_update(self, arg): + + try: + json_data = json.loads(arg) + transfer_manager = FileTransferManager(json_data['protocol'], json_data['parameterMap']) + + transfer_manager.transporter.connect() + file_name = transfer_manager.transporter.get_file() + transfer_manager.transporter.disconnect() + + agreement_content = Util.read_file(System.Ahenk.received_dir_path() + file_name) + Util.delete_file(System.Ahenk.received_dir_path() + file_name) + # TODO + title = 'Kullanıcı Sözleşmesi' + + if agreement_content is not None and agreement_content != '': + old_content = self.db_service.select_one_result('contract', 'content', + 'id =(select MAX(id) from contract)') + if old_content is None or Util.get_md5_text(old_content) != Util.get_md5_text(agreement_content): + self.db_service.update('contract', self.db_service.get_cols('contract'), + [agreement_content, title, json_data['timestamp']]) + except Exception as e: + self.logger.warning( + 'A problem occurred while updating agreement. Error Message : {0}'.format(str(e))) + + def install_plugin(self, arg): + plugin = json.loads(arg) + self.logger.debug('Installing missing plugin') + try: + plugin_name = plugin['pluginName'] + plugin_version = plugin['pluginVersion'] + + try: + transfer_manager = FileTransferManager(plugin['protocol'], plugin['parameterMap']) + transfer_manager.transporter.connect() + file_name = transfer_manager.transporter.get_file() + transfer_manager.transporter.disconnect() + downloaded_file = System.Ahenk.received_dir_path() + file_name + except Exception as e: + self.logger.error( + 'Plugin package could not fetch. Error Message: {0}.'.format(str(e))) + self.logger.error('Plugin Installation is cancelling') + self.plugin_installation_failure(plugin_name, plugin_version) + return + + try: + Util.install_with_dpkg(downloaded_file) + self.logger.debug('Plugin installed.') + except Exception as e: + self.logger.error('Could not install plugin. Error Message: {0}'.format(str(e))) + self.plugin_installation_failure(plugin_name, plugin_version) + return + + try: + Util.delete_file(downloaded_file) + self.logger.debug('Temp files were removed.') + except Exception as e: + self.logger.error('Could not remove temp file. Error Message: {0}'.format(str(e))) + + except Exception as e: + self.logger.error( + 'A problem occurred while installing new Ahenk plugin. Error Message:{0}'.format( + str(e))) + + def plugin_installation_failure(self, plugin_name, plugin_version): + + self.logger.warning('{0} plugin installation failure '.format(plugin_name)) + + if plugin_name in self.plugin_manager.delayed_profiles.keys(): + profile = self.plugin_manager.delayed_profiles[plugin_name] + self.logger.warning('An error message sending with related profile properties...') + related_policy = self.db_service.select('policy', ['version', 'execution_id'], + 'id={0}'.format(profile.get_id())) + data = dict() + data['message'] = "Profil işletilirken eklenti bulunamadı " + "ve eksik olan eklenti kurulmaya çalışırken hata ile karşılaşıldı. " + "İlgili eklenti Ahenk'e yüklendiğinde, başarısız olan bu profil " + "(Başka bir politika tarafından ezilmedikçe) " + "çalıştırılacaktır" + " Sorunu çözmek için Lider yapılandırma dosyasındaki eklenti dağıtım " + "bilgilerinin doğruluğundan ve belirtilen dizinde geçerli eklenti paketinin " + "bulunduğundan emin olun." + response = Response(type=MessageType.POLICY_STATUS.value, id=profile.get_id(), + code=MessageCode.POLICY_ERROR.value, + message="Profil işletilirken eklenti bulunamadı " + "ve eksik olan eklenti kurulurken hata oluştu", + execution_id=related_policy[0][1], policy_version=related_policy[0][0], + data=json.dumps(data), content_type=ContentType.APPLICATION_JSON.value) + messenger = Scope.get_instance().get_messenger() + messenger.send_direct_message(self.message_manager.policy_status_msg(response)) + self.logger.warning( + 'Error message was sent about {0} plugin installation failure while trying to run a profile') + + if plugin_name in self.plugin_manager.delayed_tasks.keys(): + task = self.plugin_manager.delayed_tasks[plugin_name] + self.logger.warning('An error message sending with related task properties...') + + data = dict() + data['message'] = "Görev işletilirken eklenti bulunamadı " + "ve eksik olan eklenti kurulmaya çalışırken hata ile karşılaşıldı. " + "İlgili eklenti Ahenk'e yüklendiğinde, başarısız olan bu görev " + "çalıştırılacaktır" + " Sorunu çözmek için Lider yapılandırma dosyasındaki eklenti dağıtım " + "bilgilerinin doğruluğundan ve belirtilen dizinde geçerli eklenti paketinin " + "bulunduğundan emin olun." + response = Response(type=MessageType.TASK_STATUS.value, id=task.get_id(), + code=MessageCode.TASK_ERROR.value, + message="Görev işletilirken eklenti bulunamadı " + "ve eksik olan eklenti kurulmaya çalışırken oluştu.", + data=json.dumps(data), content_type=ContentType.APPLICATION_JSON.value) + messenger = Scope.get_instance().get_messenger() + messenger.send_direct_message(self.message_manager.task_status_msg(response)) + self.logger.warning( + 'Error message was sent about {0} plugin installation failure while trying to run a task') + + def is_policy_executed(self, username): + if username in self.policy_executed: + return self.policy_executed[username] + return False + + def remove_user_executed_policy_dict(self, username): + if username in self.policy_executed: + self.policy_executed[username] = False + + def execute_default_policy(self, username): + self.logger.debug('Executing active policies for {0} user...'.format(username)) + self.task_manager.addPolicy(self.get_active_policies(username)) + + def update_scheduled_task(self, arg): + self.logger.debug('Working on scheduled task ...') + update_scheduled_json = json.loads(arg) + scheduler = Scope.get_instance().get_scheduler() + + if str(update_scheduled_json['cronExpression']).lower() == 'none' or update_scheduled_json[ + 'cronExpression'] is None: + self.logger.debug('Scheduled task will be removed') + scheduler.remove_job(int(update_scheduled_json['taskId'])) + self.logger.debug('Task removed from scheduled database') + self.db_service.update('task', ['deleted'], ['True'], + 'id={0}'.format(update_scheduled_json['taskId'])) + self.logger.debug('Task table updated.') + else: + self.logger.debug('Scheduled task cron expression will be updated.') + self.db_service.update('task', ['cron_expr'], [str(update_scheduled_json['cronExpression'])], + 'id={0}'.format(update_scheduled_json['taskId'])) + self.logger.debug('Task table updated.') + scheduler.remove_job(str(update_scheduled_json['taskId'])) + self.logger.debug('Previous scheduled task removed.') + scheduler.add_job(ScheduleTaskJob(self.get_task_bean_by_id(update_scheduled_json['taskId']))) + self.logger.debug('New scheduled task added') + + def get_task_bean_by_id(self, task_id): + + task_row = self.db_service.select('task', self.db_service.get_cols('task'), 'id={0}'.format(task_id))[0] + task = TaskBean(task_row[0], task_row[1], task_row[2], task_row[3], task_row[4], task_row[5], + self.get_plugin_bean_by_id(task_row[6]), + task_row[7], task_row[8]) + return task + + def get_plugin_bean_by_id(self, plugin_id): + plugin_row = self.db_service.select('plugin', self.db_service.get_cols('plugin'), 'id={0}'.format(plugin_id))[0] + plugin = PluginBean(plugin_row[0], plugin_row[1], plugin_row[2], plugin_row[3], plugin_row[4], plugin_row[5], + plugin_row[6], plugin_row[7], plugin_row[8], plugin_row[11], plugin_row[9], plugin_row[10], + plugin_row[12]) + return plugin + + def execute_policy(self, arg): + try: + self.logger.debug('Updating policies...') + policy = self.json_to_PolicyBean(json.loads(arg)) + self.policy_executed[policy.get_username()] = True + machine_uid = self.db_service.select_one_result('registration', 'jid', 'registered=1') + ahenk_policy_ver = self.db_service.select_one_result('policy', 'version', 'type = \'A\'') + user_policy_version = self.db_service.select_one_result('policy', 'version', + 'type = \'U\' and name = \'' + policy.get_username() + '\'') + + profile_columns = ['id', 'create_date', 'modify_date', 'label', 'description', 'overridable', 'active', + 'deleted', 'profile_data', 'plugin'] + plugin_columns = ['active', 'create_date', 'deleted', 'description', 'machine_oriented', 'modify_date', + 'name', + 'policy_plugin', 'user_oriented', 'version', 'task_plugin', 'x_based'] + + if policy.get_ahenk_policy_version() != ahenk_policy_ver: + ahenk_policy_id = self.db_service.select_one_result('policy', 'id', 'type = \'A\'') + if ahenk_policy_id is not None: + self.db_service.delete('profile', 'id=' + str(ahenk_policy_id)) + self.db_service.delete('plugin', 'id=' + str(ahenk_policy_id)) + self.db_service.update('policy', ['version', 'execution_id', 'expiration_date'], + [str(policy.get_ahenk_policy_version()), policy.agent_execution_id, + str(policy.agent_expiration_date)], 'type=\'A\'') + else: + self.db_service.update('policy', ['type', 'version', 'name', 'execution_id', 'expiration_date'], + ['A', str(policy.get_ahenk_policy_version()), machine_uid, + policy.get_agent_execution_id(), policy.agent_expiration_date]) + ahenk_policy_id = self.db_service.select_one_result('policy', 'id', 'type = \'A\'') + + for profile in policy.get_ahenk_profiles(): + plugin = profile.get_plugin() + + plugin_args = [str(plugin.get_active()), str(plugin.get_create_date()), str(plugin.get_deleted()), + str(plugin.get_description()), str(plugin.get_machine_oriented()), + str(plugin.get_modify_date()), str(plugin.get_name()), + str(plugin.get_policy_plugin()), + str(plugin.get_user_oriented()), str(plugin.get_version()), + str(plugin.get_task_plugin()), str(plugin.get_x_based())] + plugin_id = self.db_service.update('plugin', plugin_columns, plugin_args) + + profile_args = [str(ahenk_policy_id), str(profile.get_create_date()), + str(profile.get_modify_date()), + str(profile.get_label()), str(profile.get_description()), + str(profile.get_overridable()), str(profile.get_active()), + str(profile.get_deleted()), + str(profile.get_profile_data()), plugin_id] + self.db_service.update('profile', profile_columns, profile_args) + + elif ahenk_policy_ver: + self.logger.debug('Already there is ahenk policy. Command Execution Id is updating') + self.db_service.update('policy', ['execution_id'], [policy.get_agent_execution_id()], 'type = \'A\'') + else: + self.logger.debug('There is no any Ahenk policy.') + + if policy.get_user_policy_version() != user_policy_version: + user_policy_id = self.db_service.select_one_result('policy', 'id', + 'type = \'U\' and name=\'' + policy.get_username() + '\'') + if user_policy_id is not None: + # TODO remove profiles' plugins + self.db_service.delete('profile', 'id=' + str(user_policy_id)) + self.db_service.delete('plugin', 'id=' + str(user_policy_id)) + self.db_service.update('policy', ['version', 'execution_id', 'expiration_date'], + [str(policy.get_user_policy_version()), policy.user_execution_id, + str(policy.user_expiration_date)], + 'type=\'U\' and name=\'' + policy.get_username() + '\'') + else: + self.db_service.update('policy', ['type', 'version', 'name', 'execution_id', 'expiration_date'], + ['U', str(policy.get_user_policy_version()), policy.get_username(), + policy.get_user_execution_id(), policy.user_expiration_date]) + user_policy_id = self.db_service.select_one_result('policy', 'id', + 'type = \'U\' and name=\'' + policy.get_username() + '\'') + + for profile in policy.get_user_profiles(): + plugin = profile.get_plugin() + + plugin_args = [str(plugin.get_active()), str(plugin.get_create_date()), str(plugin.get_deleted()), + str(plugin.get_description()), str(plugin.get_machine_oriented()), + str(plugin.get_modify_date()), str(plugin.get_name()), + str(plugin.get_policy_plugin()), + str(plugin.get_user_oriented()), str(plugin.get_version()), + str(plugin.get_task_plugin()), str(plugin.get_x_based())] + plugin_id = self.db_service.update('plugin', plugin_columns, plugin_args) + + profile_args = [str(user_policy_id), str(profile.get_create_date()), str(profile.get_modify_date()), + str(profile.get_label()), str(profile.get_description()), + str(profile.get_overridable()), str(profile.get_active()), + str(profile.get_deleted()), + str(profile.get_profile_data()), plugin_id] + self.db_service.update('profile', profile_columns, profile_args) + + elif user_policy_version: + self.logger.debug('Already there is user policy. . Command Execution Id is updating') + self.db_service.update('policy', ['execution_id'], [policy.get_user_execution_id()], 'type = \'U\'') + else: + self.logger.debug('There is no any user policy') + + policy = self.get_active_policies(policy.get_username()) + # TODO check is null + self.task_manager.addPolicy(policy) + except Exception as e: + self.logger.error('A problem occurred while executing policy. Erroe Message: {0}:'.format(str(e))) + + def check_expiration(self, expiration): + current_timestamp = int(time.time()) * 1000 + if str(expiration) =='None': + return True + elif int(expiration) > current_timestamp: + return True + else: + return False + + def get_active_policies(self, username): + + try: + # TODO vt den gecerli son tarihi olani cek + user_policy = self.db_service.select('policy', ['id', 'version', 'name', 'expiration_date'], + ' type=\'U\' and name=\'' + username + '\'') + ahenk_policy = self.db_service.select('policy', ['id', 'version', 'expiration_date'], ' type=\'A\' ') + + plugin_columns = ['id', 'active', 'create_date', 'deleted', 'description', 'machine_oriented', + 'modify_date', + 'name', 'policy_plugin', 'user_oriented', 'version', 'task_plugin', 'x_based'] + profile_columns = ['id', 'create_date', 'label', 'description', 'overridable', 'active', 'deleted', + 'profile_data', 'modify_date', 'plugin'] + + policy = PolicyBean(username=username) + + if len(user_policy) > 0 and self.check_expiration(user_policy[0][3]): + user_policy_version = user_policy[0][0] + policy.set_user_policy_version(user_policy_version) + + user_profiles = self.db_service.select('profile', profile_columns, + ' id=' + str(user_policy_version) + ' ') + arr_profiles = [] + if len(user_profiles) > 0: + for profile in user_profiles: + plu = self.db_service.select('plugin', plugin_columns, ' id=\'' + profile[9] + '\'')[0] + plugin = PluginBean(p_id=plu[0], active=plu[1], create_date=plu[2], deleted=plu[3], + description=plu[4], machine_oriented=plu[5], modify_date=plu[6], + name=plu[7], + policy_plugin=plu[8], user_oriented=plu[9], version=plu[10], + task_plugin=plu[11], x_based=plu[12]) + + arr_profiles.append( + ProfileBean(profile[0], profile[1], profile[2], profile[3], profile[4], profile[5], + profile[6], + profile[7], profile[8], plugin, policy.get_username())) + policy.set_user_profiles(arr_profiles) + + if len(ahenk_policy) > 0 and self.check_expiration(ahenk_policy[0][2]): + ahenk_policy_version = ahenk_policy[0][0] + policy.set_ahenk_policy_version(ahenk_policy_version) + ahenk_profiles = self.db_service.select('profile', profile_columns, + ' id=' + str(ahenk_policy_version) + ' ') + arr_profiles = [] + if len(ahenk_profiles) > 0: + for profile in ahenk_profiles: + plu = self.db_service.select('plugin', plugin_columns, ' id=\'' + profile[9] + '\'')[0] + plugin = PluginBean(p_id=plu[0], active=plu[1], create_date=plu[2], deleted=plu[3], + description=plu[4], machine_oriented=plu[5], modify_date=plu[6], + name=plu[7], + policy_plugin=plu[8], user_oriented=plu[9], version=plu[10], + task_plugin=plu[11], x_based=plu[12]) + + arr_profiles.append( + ProfileBean(profile[0], profile[1], profile[2], profile[3], profile[4], profile[5], + profile[6], + profile[7], profile[8], plugin, policy.get_username())) + policy.set_ahenk_profiles(arr_profiles) + + return policy + except Exception as e: + self.logger.error('A problem occurred while getting active policies. Error Message : {0}'.format(str(e))) + + def execute_task(self, arg): + + json_task = json.loads(arg)['task'] + json_task = json.loads(json_task) + json_server_conf = json.dumps(json.loads(arg)['fileServerConf']) + + task = self.json_to_task_bean(json_task, json_server_conf) + self.logger.debug('Adding new task...Task is:{0}'.format(task.get_command_cls_id())) + + self.task_manager.addTask(task) + self.logger.debug('Task added') + + def unregister(self, msg): + j = json.loads(msg) + status = str(j['status']).lower() + + user_name = self.db_service.select_one_result('session', 'username', " 1=1 order by id desc ") + display = self.db_service.select_one_result('session', 'display', " 1=1 order by id desc ") + + if 'not_authorized' == str(status): + self.logger.info('Registration is failed. User not authorized') + Util.show_message(user_name,display,'Ahenk Lider MYS sisteminden çıkarmak için yetkili kullanıcı haklarına sahip olmanız gerekmektedir.', + 'Kullanıcı Yetkilendirme Hatası') + else : + Util.show_message(user_name, display, "Ahenk Lider MYS sisteminden çıkarılmıştır.", "") + if Util.show_message(user_name, display, "Değişikliklerin etkili olması için sistemi yeniden başlatmanız gerekmektedir.", "") : + registration= Scope.get_instance().get_registration() + registration.purge_and_unregister() + + + def json_to_task_bean(self, json_data, file_server_conf=None): + plu = json_data['plugin'] + plugin = PluginBean(p_id=plu['id'], active=plu['active'], create_date=plu['createDate'], deleted=plu['deleted'], + description=plu['description'], machine_oriented=plu['machineOriented'], + modify_date=plu['modifyDate'], name=plu['name'], policy_plugin=plu['policyPlugin'], + user_oriented=plu['userOriented'], version=plu['version'], task_plugin=plu['taskPlugin'], + x_based=plu['xBased']) + return TaskBean(_id=json_data['id'], create_date=json_data['createDate'], modify_date=json_data['modifyDate'], + command_cls_id=json_data['commandClsId'], parameter_map=json_data['parameterMap'], + deleted=json_data['deleted'], plugin=plugin, cron_str=json_data['cronExpression'], + file_server=str(file_server_conf)) + + def execute_script(self, arg): + try: + self.logger.debug('Executing script...') + messenger = Scope().get_instance().get_messenger() + + json_data = json.loads(arg) + result_code, p_out, p_err = Util.execute(str(json_data['command'])) + + self.logger.debug('Executed script') + + data = dict() + data['type'] = 'SCRIPT_RESULT' + data['timestamp'] = str(Util.timestamp()) + + if result_code == 0: + self.logger.debug('Command execution was finished successfully') + try: + temp_name = str(Util.generate_uuid()) + temp_full_path = System.Ahenk.received_dir_path() + temp_name + self.logger.debug('Writing result to file') + Util.write_file(temp_full_path, str(p_out)) + md5 = Util.get_md5_file(temp_full_path) + Util.rename_file(temp_full_path, System.Ahenk.received_dir_path() + md5) + + file_manager = FileTransferManager(json_data['fileServerConf']['protocol'], + json_data['fileServerConf']['parameterMap']) + file_manager.transporter.connect() + self.logger.debug('File transfer connection was created') + success = file_manager.transporter.send_file(System.Ahenk.received_dir_path() + md5, md5) + self.logger.debug('File was transferred') + file_manager.transporter.disconnect() + self.logger.debug('File transfer connection was closed') + + if success is False: + self.logger.error('A problem occurred while file transferring') + data['resultCode'] = '-1' + data[ + 'errorMessage'] = 'Command executed successfully but a problem occurred while sending result file' + + else: + data['md5'] = md5 + + except Exception as e: + self.logger.error( + 'A problem occurred while file transferring. Error Message :{0}'.format( + str(e))) + raise + else: + self.logger.error( + 'Command execution was failed. Error Message :{0}'.format(str(result_code))) + data['resultCode'] = str(result_code) + data['errorMessage'] = str(p_err) + + messenger.send_direct_message(json.dumps(data)) + except Exception as e: + self.logger.error( + 'A problem occurred while running execute script action. Error Message :{0}'.format( + str(e))) + + def json_to_PolicyBean(self, json_data): + + username = json_data['username'] + ahenk_prof_json_arr = json_data['agentPolicyProfiles'] + user_prof_json_arr = json_data['userPolicyProfiles'] + + ahenk_prof_arr = [] + user_prof_arr = [] + if ahenk_prof_json_arr is not None: + for prof in ahenk_prof_json_arr: + plu = json.loads(json.dumps(prof['plugin'])) + plugin = PluginBean(p_id=plu['id'], active=plu['active'], create_date=plu['createDate'], + deleted=plu['deleted'], description=plu['description'], + machine_oriented=plu['machineOriented'], modify_date=plu['modifyDate'], + name=plu['name'], policy_plugin=plu['policyPlugin'], + user_oriented=plu['userOriented'], version=plu['version'], + task_plugin=plu['taskPlugin'], x_based=plu['xBased']) + ahenk_prof_arr.append( + ProfileBean(prof['id'], prof['createDate'], prof['label'], prof['description'], prof['overridable'], + prof['active'], prof['deleted'], json.dumps(prof['profileData']), prof['modifyDate'], + plugin, username)) + + if user_prof_json_arr is not None: + for prof in user_prof_json_arr: + plu = json.loads(json.dumps(prof['plugin'])) + plugin = PluginBean(p_id=plu['id'], active=plu['active'], create_date=plu['createDate'], + deleted=plu['deleted'], description=plu['description'], + machine_oriented=plu['machineOriented'], modify_date=plu['modifyDate'], + name=plu['name'], policy_plugin=plu['policyPlugin'], + user_oriented=plu['userOriented'], version=plu['version'], + task_plugin=plu['taskPlugin'], x_based=plu['xBased']) + user_prof_arr.append( + ProfileBean(prof['id'], prof['createDate'], prof['label'], prof['description'], prof['overridable'], + prof['active'], prof['deleted'], json.dumps(prof['profileData']), prof['modifyDate'], + plugin, username)) + + return PolicyBean(ahenk_policy_version=json_data['agentPolicyVersion'], + user_policy_version=json_data['userPolicyVersion'], ahenk_profiles=ahenk_prof_arr, + user_profiles=user_prof_arr, timestamp=json_data['timestamp'], username=json_data['username'], + agent_execution_id=json_data['agentCommandExecutionId'], + user_execution_id=json_data['userCommandExecutionId'], + agent_expiration_date=json_data['agentPolicyExpirationDate'], + user_expiration_date=json_data['userPolicyExpirationDate']) + + def login_response(self, msg): + jData = json.loads(msg) + username = jData['userName'] + if username is not None: + self.create_sudo_polkit(username) + + + def create_sudo_polkit(self,username): + content = "[Configuration] \nAdminIdentities=unix-user:{}".format(username) + ahenk_policy_file = "/etc/polkit-1/localauthority.conf.d/99-ahenk-policy.conf" + if not Util.is_exist(ahenk_policy_file): + Util.create_file(ahenk_policy_file) + Util.write_file(ahenk_policy_file, content) + self.logger.debug('Ahenk polkit file created and user added.. User : {}'.format(username)) + else: + self.logger.debug('Writing result to file') + Util.delete_file(ahenk_policy_file) + Util.create_file(ahenk_policy_file) + Util.write_file(ahenk_policy_file, content) \ No newline at end of file diff --git a/usr/share/ahenk/base/file/file_transfer_manager.py b/usr/share/ahenk/base/file/file_transfer_manager.py new file mode 100644 index 0000000..dfee1c4 --- /dev/null +++ b/usr/share/ahenk/base/file/file_transfer_manager.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +from base.scope import Scope +from base.file.ssh_file_transfer import Ssh +from base.file.http_file_transfer import Http + + +class FileTransferManager(object): + def __init__(self, protocol, parameter_map): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.configuration_manager = scope.get_configuration_manager() + self.transporter = self.get_instance(protocol, parameter_map) + + def get_instance(self, protocol, parameter_map): + try: + transporter = None + if str(protocol).lower() == 'ssh': + transporter = Ssh(parameter_map) + elif str(protocol).lower() == 'http': + transporter = Http(parameter_map) + else: + raise Exception('Unsupported file transfer protocol: {0}'.format(str(protocol))) + return transporter + + except Exception as e: + self.logger.error( + 'A problem occurred while getting instance of related protocol. Error Message: {0}'.format( + str(e))) + return None diff --git a/usr/share/ahenk/base/file/http_file_transfer.py b/usr/share/ahenk/base/file/http_file_transfer.py new file mode 100644 index 0000000..80f3ad7 --- /dev/null +++ b/usr/share/ahenk/base/file/http_file_transfer.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin +from base.util.util import Util +from base.system.system import System +import urllib.request + +from base.scope import Scope + + +class Http(object): + def __init__(self, parameter_map): + + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.configuration_manager = scope.get_configuration_manager() + try: + self.url = parameter_map['url'] + except Exception as e: + self.logger.error( + 'A problem occurred while parsing parameter map. Error Message: {0}'.format(str(e))) + + def send_file(self, local_path, md5): + pass + + def get_file(self): + + self.logger.debug('[FileTransfer] Getting file ...') + file_md5 = None + try: + tmp_file_name = str(Util.generate_uuid()) + local_full_path = System.Ahenk.received_dir_path() + tmp_file_name + urllib.request.urlretrieve(self.url, local_full_path) + file_md5 = str(Util.get_md5_file(local_full_path)) + Util.rename_file(local_full_path, System.Ahenk.received_dir_path() + file_md5) + self.logger.debug('File was downloaded to {0} from {1}'.format(local_full_path, self.url)) + except Exception as e: + self.logger.error( + 'A problem occurred while downloading file. Exception message: {0}'.format(str(e))) + raise + return file_md5 + + def disconnect(self): + pass + + def is_connected(self): + pass + + def connect(self): + pass diff --git a/usr/share/ahenk/base/file/ssh_file_transfer.py b/usr/share/ahenk/base/file/ssh_file_transfer.py new file mode 100644 index 0000000..4b85788 --- /dev/null +++ b/usr/share/ahenk/base/file/ssh_file_transfer.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +from base.util.util import Util +from base.system.system import System +from base.scope import Scope +import paramiko +import logging + + +class Ssh(object): + def __init__(self, parameter_map): + + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.configuration_manager = scope.get_configuration_manager() + logging.getLogger("paramiko").setLevel(logging.INFO) + + try: + self.target_hostname = parameter_map['host'] + self.target_port = parameter_map['port'] + self.target_username = parameter_map['username'] + self.target_path = parameter_map['path'] + self.target_password = None + self.p_key = None + if Util.has_attr_json(parameter_map, 'password'): + self.target_password = parameter_map['password'] + else: + self.p_key = parameter_map['pkey'] + except Exception as e: + self.logger.error( + 'A problem occurred while parsing ssh connection parameters. Error Message: {0}'.format(str(e))) + + self.connection = None + self.logger.debug('Parameters set up') + + def send_file(self, local_path, md5): + + self.logger.debug(' {0} is sending to {1}'.format(local_path, self.target_path + md5)) + try: + sftp = paramiko.SFTPClient.from_transport(self.connection) + try: + sftp.chdir(self.target_path) # Test if remote_path exists + except IOError: + sftp.mkdir(self.target_path) # Create remote_path + sftp.chdir(self.target_path) + + sftp.put(local_path, self.target_path + md5) + self.logger.debug('File was sent to {0} from {1}'.format(local_path, self.target_path)) + return True + except Exception as e: + self.logger.error('A problem occurred while sending file. Exception message: {0}'.format(str(e))) + return False + + def get_file(self, local_path=None, file_name=None): + self.logger.debug('Getting file ...') + try: + tmp_file_name = str(Util.generate_uuid()) + local_full_path = System.Ahenk.received_dir_path() + tmp_file_name + sftp = paramiko.SFTPClient.from_transport(self.connection) + sftp.get(self.target_path, local_full_path) + + if local_path is None: + receive_path = System.Ahenk.received_dir_path() + else: + receive_path = local_path + if file_name is not None: + f_name = file_name + else: + f_name = str(Util.get_md5_file(local_full_path)) + Util.rename_file(local_full_path, receive_path + f_name) + self.logger.debug('File was downloaded to {0} from {1}'.format(receive_path, self.target_path)) + except Exception as e: + self.logger.warning('A problem occurred while downloading file. Exception message: {0}'.format(str(e))) + raise + return f_name + + def connect(self): + self.logger.debug('Connecting to {0} via {1}'.format(self.target_hostname, self.target_port)) + try: + connection = paramiko.Transport(self.target_hostname, int(self.target_port)) + connection.connect(username=self.target_username, password=self.target_password, pkey=self.p_key) + self.connection = connection + self.logger.debug('Connected.') + except Exception as e: + self.logger.error( + 'A problem occurred while connecting to {0} . Exception message: {1}'.format( + self.target_hostname, str(e))) + + def disconnect(self): + self.connection.close() + self.logger.debug('Connection is closed.') + # TODO + pass + + def is_connected(self): + # TODO + pass diff --git a/usr/share/ahenk/base/logger/__init__.py b/usr/share/ahenk/base/logger/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/logger/ahenk_logger.py b/usr/share/ahenk/base/logger/ahenk_logger.py new file mode 100644 index 0000000..165f440 --- /dev/null +++ b/usr/share/ahenk/base/logger/ahenk_logger.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +# Author: Volkan Şahin + +import logging +import logging.config +from inspect import getframeinfo, stack +import sys + +from base.scope import Scope + + +class Logger(object): + """Ahenk Logger Service + This module is Ahenk logger service implementation. + Instance is stored in Scope. Service adds name of script which use logger, with line number. + + Example usage: + logger = Scope.getInstance().get_logger() + logger.debug('') + logger.info('') + logger.warning('') + + logger.error('') + + """ + + def __init__(self): + super(Logger, self).__init__() + scope = Scope.get_instance() + config_manger = scope.get_configuration_manager() + + logging.config.fileConfig(config_manger.get('BASE', 'logConfigurationFilePath')) + self.logger = logging.getLogger() + + def get_logger(self): + return self.logger + + def debug(self, message): + caller = getframeinfo(stack()[1][0]) + self.logger.debug('[{0} {1}]\t {2}'.format(self.get_log_header(caller.filename), caller.lineno, message)) + + def info(self, message): + caller = getframeinfo(stack()[1][0]) + self.logger.info('[{0} {1}]\t {2}'.format(self.get_log_header(caller.filename), caller.lineno, message)) + + def warning(self, message): + caller = getframeinfo(stack()[1][0]) + self.logger.warning('[{0} {1}]\t {2}'.format(self.get_log_header(caller.filename), caller.lineno, message)) + + def error(self, message): + try: + exc_type, exc_value, exc_trace_back = sys.exc_info() + caller = getframeinfo(stack()[1][0]) + + if exc_type is None and exc_value is None and exc_trace_back is None: + self.logger.error('[{0} {1}]\t {2}'.format(self.get_log_header(caller.filename), caller.lineno, message)) + else: + self.logger.error( + '[{0} {1} {2}]\t {3}'.format(self.get_log_header(caller.filename), exc_trace_back.tb_lineno, exc_type, + message)) + except Exception as e: + self.logger.error(message) + + @staticmethod + def get_log_header(file_path): + + if file_path is not None: + name_list = file_path.split('/') + result = '' + if len(name_list) > 1: + result = str(name_list[len(name_list) - 2]).upper() + ' >> ' + name_list[len(name_list) - 1] + + return result + else: + return None diff --git a/usr/share/ahenk/base/mail/mail_manager.py b/usr/share/ahenk/base/mail/mail_manager.py new file mode 100644 index 0000000..ef339e9 --- /dev/null +++ b/usr/share/ahenk/base/mail/mail_manager.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import smtplib +import os +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.mime.base import MIMEBase +from email.utils import formatdate +from email import encoders +from base.scope import Scope + + +class Mail: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.configuration_manager = scope.get_configuration_manager() + try: + self.smtp_host = self.configuration_manager.get('MAIL', 'smtp_host') + self.smtp_port = int(self.configuration_manager.get('MAIL', 'smtp_port')) + self.from_username = self.configuration_manager.get('MAIL', 'from_username') + self.from_password = self.configuration_manager.get('MAIL', 'from_password') + self.to_address = self.configuration_manager.get('MAIL', 'to_address') + + except Exception as e: + self.logger.error( + 'A problem occurred while reading mail server parameters from conf file. Error Message: {0}'.format( + e)) + self.server = None + self.logger.debug('Mail service initialized.') + + def connect(self): + self.logger.debug('Connecting to SMTP server') + self.server = smtplib.SMTP(self.smtp_host, self.smtp_port) + self.logger.debug('Setting debug level') + self.server.set_debuglevel(2) + self.logger.debug('Using tls if server supports') + self.server.starttls() + self.logger.debug('Loging in to smtp server as {0}'.format(self.from_username)) + self.server.login(self.from_username, self.from_password) + + def disconnect(self): + self.logger.debug('Disconnecting from mail server') + self.server.quit() + self.logger.debug('Disconnected') + + def send_mail(self, subject, message, files=None): + + if files is None: + files = [] + + msg = MIMEMultipart() + msg['Date'] = formatdate(localtime=True) + msg['Subject'] = subject + + msg.attach(MIMEText(message)) + + # TODO files attachment max size + if files is not None: + for f in files: + part = MIMEBase('application', "octet-stream") + part.set_payload(open(f, "rb").read()) + encoders.encode_base64(part) + part.add_header('Content-Disposition', 'attachment; filename="{0}"'.format(os.path.basename(f))) + msg.attach(part) + + self.logger.debug('Sending mail to {0} {1}'.format(self.to_address, ' about {0}'.format(subject))) + self.server.sendmail(self.from_username, self.to_address, msg.as_string()) + self.logger.debug('Mail was sent.') diff --git a/usr/share/ahenk/base/messaging/__init__.py b/usr/share/ahenk/base/messaging/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/messaging/anonymous_messenger.py b/usr/share/ahenk/base/messaging/anonymous_messenger.py new file mode 100644 index 0000000..e2012da --- /dev/null +++ b/usr/share/ahenk/base/messaging/anonymous_messenger.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin +# Author: İsmail BAŞARAN +import json +import sys +import socket +from base.util.util import Util +import time +from base.system.system import System + +import pwd +import os + +from helper import system as sysx + +from sleekxmpp import ClientXMPP +from base.scope import Scope + +sys.path.append('../..') + + +class AnonymousMessenger(ClientXMPP): + def __init__(self, message, host= None, servicename= None): + # global scope of ahenk + scope = Scope().get_instance() + + self.logger = scope.get_logger() + self.configuration_manager = scope.get_configuration_manager() + self.registration = scope.get_registration() + self.event_manager = scope.get_event_manager() + + if host is not None and servicename is not None: + self.host = str(host) + self.service = str(servicename) + self.port = str(self.configuration_manager.get('CONNECTION', 'port')) + + # self.host = str(socket.gethostbyname(self.configuration_manager.get('CONNECTION', 'host'))) + # self.service = str(self.configuration_manager.get('CONNECTION', 'servicename')) + # self.port = str(self.configuration_manager.get('CONNECTION', 'port')) + + ClientXMPP.__init__(self, self.service, None) + + self.message = message + self.receiver_resource = self.configuration_manager.get('CONNECTION', 'receiverresource') + self.receiver = self.configuration_manager.get('CONNECTION','receiverjid') + '@' + self.configuration_manager.get('CONNECTION', 'servicename') + if self.receiver_resource: + self.receiver += '/' + self.receiver_resource + + if self.configuration_manager.get('CONNECTION', 'use_tls').strip().lower() == 'true': + self.use_tls = True + else: + self.use_tls = False + + self.logger.debug('XMPP Receiver parameters were set') + + self.add_listeners() + self.register_extensions() + + def add_listeners(self): + self.add_event_handler("session_start", self.session_start) + self.add_event_handler("message", self.recv_direct_message) + self.logger.debug('Event handlers were added') + + def session_start(self, event): + self.logger.debug('Session was started') + self.get_roster() + self.send_presence() + + if self.message is not None: + self.send_direct_message(self.message) + + def register_extensions(self): + try: + self.register_plugin('xep_0030') # Service Discovery + self.register_plugin('xep_0199') # XMPP Ping + + self.logger.debug('Extension were registered: xep_0030,xep_0199') + return True + except Exception as e: + self.logger.error('Extension registration is failed! Error Message: {0}'.format(str(e))) + return False + + def connect_to_server(self): + try: + self.logger.debug('Connecting to server...') + self['feature_mechanisms'].unencrypted_plain = True + self.connect((self.host, self.port), use_tls=self.use_tls) + self.process(block=True) + self.logger.debug('Connection were established successfully') + return True + except Exception as e: + self.logger.error('Connection to server is failed! Error Message: {0}'.format(str(e))) + return False + + def recv_direct_message(self, msg): + if msg['type'] in ['normal']: + self.logger.debug('---------->Received message: {0}'.format(str(msg['body']))) + self.logger.debug('Reading registration reply') + j = json.loads(str(msg['body'])) + message_type = j['type'] + status = str(j['status']).lower() + dn = str(j['agentDn']) + self.logger.debug('Registration status: ' + str(status)) + + if 'not_authorized' == str(status): + self.logger.info('Registration is failed. User not authorized') + Util.show_message(os.getlogin(), ':0','Ahenk Lider MYS sistemine alınamadı !! Sadece yetkili kullanıcılar kayıt yapabilir.', 'Kullanıcı Yetkilendirme Hatası') + self.logger.debug('Disconnecting...') + self.disconnect() + + elif 'already_exists' == str(status) or 'registered' == str(status) or 'registered_without_ldap' == str(status): + try: + self.logger.info('Registred from server. Registration process starting.') + self.event_manager.fireEvent('REGISTRATION_SUCCESS', j) + msg = str(self.host) + " Etki Alanına hoş geldiniz." + Util.show_message(os.getlogin(), ':0' ,msg, "UYARI") + msg = "Değişikliklerin etkili olması için sistem yeniden başlayacaktır. Sistem yeniden başlatılıyor...." + Util.show_message(os.getlogin(), ':0',msg, "UYARI") + time.sleep(3) + self.logger.info('Disconnecting...') + self.disconnect() + self.logger.info('Rebooting...') + #System.Process.kill_by_pid(int(System.Ahenk.get_pid_number())) + #sys.exit(2) + Util.shutdown(); + + except Exception as e: + self.logger.error('Error Message: {0}.'.format(str(e))) + Util.show_message(os.getlogin(), ':0',str(e)) + self.logger.debug('Disconnecting...') + self.disconnect() + + + elif 'registration_error' == str(status): + self.logger.info('Registration is failed. New registration request will send') + #self.event_manager.fireEvent('REGISTRATION_ERROR', str(j)) + Util.show_message(os.getlogin(), ':0','Ahenk Lider MYS sistemine alınamadı !! Kayıt esnasında hata oluştu. Lütfen sistem yöneticinize başvurunuz.', + 'Sistem Hatası') + self.logger.debug('Disconnecting...') + self.disconnect() + else: + self.event_manger.fireEvent(message_type, str(msg['body'])) + self.logger.debug('Fired event is: {0}'.format(message_type)) + + def send_direct_message(self, msg): + self.logger.debug('<<--------Sending message: {0}'.format(msg)) + self.send_message(mto=self.receiver, mbody=msg, mtype='normal') + + diff --git a/usr/share/ahenk/base/messaging/message_response_queue.py b/usr/share/ahenk/base/messaging/message_response_queue.py new file mode 100644 index 0000000..95bcbb4 --- /dev/null +++ b/usr/share/ahenk/base/messaging/message_response_queue.py @@ -0,0 +1,33 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN + +import threading + +from base.scope import Scope + + +class MessageResponseQueue(threading.Thread): + """ + This class handles responses and sends it to lider server. + """ + + def __init__(self, outQueue): + super(MessageResponseQueue, self).__init__() + scope = Scope.get_instance() + self.logger = scope.get_logger() + self.messageManager = scope.get_messenger() + self.outQueue = outQueue + + def run(self): + while True: + try: + # This item will send response to lider. + # item must be response message. Response message may be generic message type + responseMessage = self.outQueue.get(block=True) + self.logger.debug('Sending response message to lider. Response Message ' + str(responseMessage)) + # Call message manager for response + self.messageManager.send_direct_message(responseMessage) + # self.outQueue.task_done() + except Exception as e: + self.logger.error diff --git a/usr/share/ahenk/base/messaging/messaging.py b/usr/share/ahenk/base/messaging/messaging.py new file mode 100644 index 0000000..888811f --- /dev/null +++ b/usr/share/ahenk/base/messaging/messaging.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin +import json + +from base.scope import Scope +from base.system.system import System +from base.util.util import Util +import os + + +# TODO Message Factory +class Messaging(object): + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.conf_manager = scope.get_configuration_manager() + self.db_service = scope.get_db_service() + self.event_manger = scope.get_event_manager() + + def missing_plugin_message(self, plugin): + data = dict() + data['type'] = 'MISSING_PLUGIN' + data['pluginName'] = plugin.get_name() + data['pluginVersion'] = plugin.get_version() + + json_data = json.dumps(data) + self.logger.debug('Missing plugin message was created') + return str(json_data) + + def task_status_msg(self, response): + data = dict() + data['type'] = response.get_type() + data['taskId'] = response.get_id() + data['responseCode'] = response.get_code() + data['responseMessage'] = response.get_message() + response_data = None + if response.get_data() is not None: + response_data = json.loads(str(response.get_data())) + data['responseData'] = response_data + data['contentType'] = response.get_content_type() + data['timestamp'] = response.get_timestamp() + + json_data = json.dumps(data) + self.logger.debug('Task status message was created') + return str(json_data) + + def policy_status_msg(self, response): + data = dict() + data['type'] = response.get_type() + data['policyVersion'] = response.get_policy_version() + data['commandExecutionId'] = response.get_execution_id() + data['responseCode'] = response.get_code() + data['responseMessage'] = response.get_message() + + response_data = None + if response.get_data() is not None: + response_data = json.loads(str(response.get_data())) + + data['responseData'] = response_data + data['contentType'] = response.get_content_type() + data['timestamp'] = response.get_timestamp() + + json_data = json.dumps(data) + self.logger.debug('Policy status message was created') + return str(json_data) + + def login_msg(self, username,ip=None): + data = dict() + data['type'] = 'LOGIN' + data['username'] = username + data['ipAddresses'] = str(System.Hardware.Network.ip_addresses()).replace('[', '').replace(']', '') + data['timestamp'] = Util.timestamp() + data['userIp'] = ip + data['hostname'] = str(System.Os.hostname()) + + self.logger.debug('USER IP : '+ str(ip)+ ' IPADDRESSES : '+ str(System.Hardware.Network.ip_addresses()).replace('[', '').replace(']', '')) + + + data['hardware.monitors'] = str(System.Hardware.monitors()), + data['hardware.screens'] = str(System.Hardware.screens()), + data['hardware.usbDevices'] = str(System.Hardware.usb_devices()), + data['hardware.printers'] = str(System.Hardware.printers()), + data['hardware.systemDefinitions'] = str(System.Hardware.system_definitions()), + + json_data = json.dumps(data) + self.logger.debug('Login message was created') + return json_data + + def logout_msg(self, username,ip): + data = dict() + data['type'] = 'LOGOUT' + data['username'] = str(username) + data['timestamp'] = Util.timestamp() + data['userIp'] = ip + json_data = json.dumps(data) + self.logger.debug('Logout message was created') + return json_data + + def policy_request_msg(self, username): + data = dict() + data['type'] = 'GET_POLICIES' + + user_policy_number = self.db_service.select_one_result('policy', 'version', + 'type = \'U\' and name = \'' + username + '\'') + machine_policy_number = self.db_service.select_one_result('policy', 'version', 'type = \'A\'') + + data['userPolicyVersion'] = user_policy_number + data['agentPolicyVersion'] = machine_policy_number + + data['username'] = str(username) + data['timestamp'] = Util.timestamp() + json_data = json.dumps(data) + self.logger.debug('Get Policies message was created') + return json_data + + def registration_msg(self, userName= None, userPassword=None): + data = dict() + data['type'] = 'REGISTER' + data['from'] = self.db_service.select_one_result('registration', 'jid', ' 1=1') + data['password'] = self.db_service.select_one_result('registration', 'password', ' 1=1') + + params = self.db_service.select_one_result('registration', 'params', ' 1=1') + data['data'] = json.loads(str(params)) + json_params = json.loads(str(params)) + data['macAddresses'] = json_params['macAddresses'] + data['ipAddresses'] = json_params['ipAddresses'] + data['hostname'] = json_params['hostname'] + + if userName is not None: + data["userName"] = str(userName) + + if userPassword is not None: + data["userPassword"] = str(userPassword) + + data['timestamp'] = self.db_service.select_one_result('registration', 'timestamp', ' 1=1') + json_data = json.dumps(data) + self.logger.debug('Registration message was created') + return json_data + + def ldap_registration_msg(self): + data = dict() + data['type'] = 'REGISTER_LDAP' + data['from'] = str(self.conf_manager.get('REGISTRATION', 'from')) + data['password'] = str(self.conf_manager.get('REGISTRATION', 'password')) + data['macAddresses'] = str(self.conf_manager.get('REGISTRATION', 'macAddresses')) + data['ipAddresses'] = str(self.conf_manager.get('REGISTRATION', 'ipAddresses')) + data['hostname'] = str(self.conf_manager.get('REGISTRATION', 'hostname')) + data['timestamp'] = Util.timestamp() + json_data = json.dumps(data) + self.logger.debug('LDAP Registration message was created') + return json_data + + def unregister_msg(self): + + user_name = self.db_service.select_one_result('session', 'username') + display = self.db_service.select_one_result('session', 'display') + + self.logger.debug('User : ' + str(user_name)) + + pout = Util.show_unregistration_message(user_name,display, + 'Makineyi etki alanından çıkarmak için zorunlu alanları giriniz. Lütfen DEVAM EDEN İŞLEMLERİNİZİ sonlandırdığınıza emin olunuz !', + 'ETKI ALANINDAN ÇIKARMA') + + self.logger.debug('pout : ' + str(pout)) + + field_values = pout.split(' ') + + user_registration_info = list(field_values) + + data = dict() + data['type'] = 'UNREGISTER' + data['from'] = str(self.conf_manager.get('CONNECTION', 'uid')) + data['password'] = str(self.conf_manager.get('CONNECTION', 'password')) + + data['userName'] = user_registration_info[0]; + data['userPassword'] = user_registration_info[1]; + + #data['macAddresses'] = str(self.conf_manager.get('REGISTRATION', 'macAddresses')) + #data['ipAddresses'] = str(self.conf_manager.get('REGISTRATION', 'ipAddresses')) + #data['hostname'] = str(self.conf_manager.get('REGISTRATION', 'hostname')) + # data['username'] = str(pwd.getpwuid( os.getuid() )[ 0 ]) + data['timestamp'] = Util.timestamp() + json_data = json.dumps(data) + self.logger.debug('Unregister message was created') + return json_data + + def agreement_request_msg(self): + data = dict() + data['type'] = 'REQUEST_AGREEMENT' + + """ + contract_content = self.db_service.select_one_result('contract', 'content', 'id =(select MAX(id) from contract)') + if contract_content is not None and contract_content != '': + data['md5'] = Util.get_md5_text(contract_content) + else: + data['md5'] = '' + """ + + data['timestamp'] = Util.timestamp() + json_data = json.dumps(data) + self.logger.debug('Agreement request message was created') + return json_data + + def agreement_answer_msg(self, username, answer): + data = dict() + data['type'] = 'AGREEMENT_STATUS' + data['username'] = username + data['accepted'] = answer + data['timestamp'] = Util.timestamp() + contract_content = self.db_service.select_one_result('contract', 'content', + 'id =(select MAX(id) from contract)') + if contract_content is not None and contract_content != '': + data['md5'] = Util.get_md5_text(contract_content) + else: + data['md5'] = '' + + json_data = json.dumps(data) + self.logger.debug('Agreement answer message was created') + return json_data diff --git a/usr/share/ahenk/base/messaging/messenger.py b/usr/share/ahenk/base/messaging/messenger.py new file mode 100644 index 0000000..161b647 --- /dev/null +++ b/usr/share/ahenk/base/messaging/messenger.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin +# Author: İsmail BAŞARAN +import json +import sys +import socket +from sleekxmpp import ClientXMPP + +from base.scope import Scope + +sys.path.append('../..') + + +class Messenger(ClientXMPP): + def __init__(self): + scope = Scope().get_instance() + + self.logger = scope.get_logger() + self.configuration_manager = scope.get_configuration_manager() + self.event_manger = scope.get_event_manager() + self.execution_manager = scope.get_execution_manager() + + self.my_jid = str( + self.configuration_manager.get('CONNECTION', 'uid') + '@' + self.configuration_manager.get('CONNECTION', + 'servicename')) + self.my_pass = str(self.configuration_manager.get('CONNECTION', 'password')) + + ClientXMPP.__init__(self, self.my_jid, self.my_pass) + + self.auto_authorize = True + self.auto_subscribe = True + + self.hostname = str(socket.gethostbyname(self.configuration_manager.get('CONNECTION', 'host'))) + self.receiver_resource = self.configuration_manager.get('CONNECTION', 'receiverresource') + + if self.configuration_manager.get('CONNECTION', 'use_tls').strip().lower() == 'true': + self.use_tls = True + else: + self.use_tls = False + + self.receiver = self.configuration_manager.get('CONNECTION', + 'receiverjid') + '@' + self.configuration_manager.get( + 'CONNECTION', 'servicename') + + if self.receiver_resource: + self.receiver += '/' + self.receiver_resource + + self.logger.debug('XMPP Messager parameters were set') + + self.register_extensions() + self.add_listeners() + + def register_extensions(self): + try: + self.register_plugin('xep_0030') # Service Discovery + self.register_plugin('xep_0199') # XMPP Ping + + self.logger.debug('[Messenger]Extension were registered: xep_0030,xep_0199') + return True + except Exception as e: + self.logger.error('[Messenger]Extension registration is failed! Error Message: {0}'.format(str(e))) + return False + + def add_listeners(self): + self.add_event_handler('session_start', self.session_start) + self.add_event_handler('session_end', self.session_end) + self.add_event_handler('message', self.recv_direct_message) + + self.logger.debug('Event handlers were added') + + def connect_to_server(self): # Connect to the XMPP server and start processing XMPP stanzas. + try: + self['feature_mechanisms'].unencrypted_plain = True + + self.connect((self.hostname, 5222), use_tls=self.use_tls) + self.process(block=False) + self.logger.debug('Connection were established successfully') + return True + except Exception as e: + self.logger.error('Connection to server is failed! Error Message: {0}'.format(str(e))) + return False + + def session_end(self): + self.logger.warning('DISCONNECTED') + + def session_start(self, event): + self.logger.debug('Session was started') + self.get_roster() + self.send_presence() + + def send_direct_message(self, msg): + try: + self.logger.info('<<--------Sending message: {0}'.format(msg)) + self.send_message(mto=self.receiver, mbody=msg, mtype='normal') + except Exception as e: + self.logger.error( + 'A problem occurred while sending direct message. Error Message: {0}'.format(str(e))) + + def recv_direct_message(self, msg): + if msg['type'] in ['normal']: + self.logger.info('---------->Received message: {0}'.format(str(msg['body']))) + try: + j = json.loads(str(msg['body'])) + message_type = j['type'] + self.event_manger.fireEvent(message_type, str(msg['body'])) + self.logger.debug('Fired event is: {0}'.format(message_type)) + except Exception as e: + self.logger.error( + 'A problem occurred while keeping message. Error Message: {0}'.format(str(e))) diff --git a/usr/share/ahenk/base/model/__init__.py b/usr/share/ahenk/base/model/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/model/enum/__init__.py b/usr/share/ahenk/base/model/enum/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/model/enum/content_type.py b/usr/share/ahenk/base/model/enum/content_type.py new file mode 100644 index 0000000..aa905a4 --- /dev/null +++ b/usr/share/ahenk/base/model/enum/content_type.py @@ -0,0 +1,15 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin +from enum import Enum + + +class ContentType(Enum): + APPLICATION_MS_WORD = 'APPLICATION_MS_WORD' + APPLICATION_PDF = 'APPLICATION_PDF' + APPLICATION_VND_MS_EXCEL = 'APPLICATION_VND_MS_EXCEL' + IMAGE_JPEG = 'IMAGE_JPEG' + IMAGE_PNG = 'IMAGE_PNG' + TEXT_HTML = 'TEXT_HTML' + TEXT_PLAIN = 'TEXT_PLAIN' + APPLICATION_JSON = 'APPLICATION_JSON' diff --git a/usr/share/ahenk/base/model/enum/message_code.py b/usr/share/ahenk/base/model/enum/message_code.py new file mode 100644 index 0000000..f740fa2 --- /dev/null +++ b/usr/share/ahenk/base/model/enum/message_code.py @@ -0,0 +1,20 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin +from enum import Enum + + +class MessageCode(Enum): + POLICY_ERROR = 'POLICY_ERROR' + POLICY_KILLED = 'POLICY_KILLED' + POLICY_PROCESSED = 'POLICY_PROCESSED' + POLICY_RECEIVED = 'POLICY_RECEIVED' + POLICY_TIMEOUT = 'POLICY_TIMEOUT' + POLICY_WARNING = 'POLICY_WARNING' + TASK_ERROR = 'TASK_ERROR' + TASK_KILLED = 'TASK_KILLED' + TASK_PROCESSED = 'TASK_PROCESSED' + TASK_PROCESSING = 'TASK_PROCESSING' + TASK_TIMEOUT = 'TASK_TIMEOUT' + TASK_WARNING = 'TASK_WARNING' + TASK_RECEIVED = 'TASK_RECEIVED' diff --git a/usr/share/ahenk/base/model/enum/message_type.py b/usr/share/ahenk/base/model/enum/message_type.py new file mode 100644 index 0000000..a67814c --- /dev/null +++ b/usr/share/ahenk/base/model/enum/message_type.py @@ -0,0 +1,27 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +from enum import Enum + + +class MessageType(Enum): + EXECUTE_POLICY = 'EXECUTE_POLICY' + EXECUTE_SCRIPT = 'EXECUTE_SCRIPT' + EXECUTE_TASK = 'EXECUTE_TASK' + GET_POLICIES = 'GET_POLICIES' + INSTALL_PLUGIN = 'INSTALL_PLUGIN' + LOGIN = 'LOGIN' + LOGOUT = 'LOGOUT' + MISSING_PLUGIN = 'MISSING_PLUGIN' + MOVE_FILE = 'MOVE_FILE' + POLICY_STATUS = 'POLICY_STATUS' + REGISTER = 'REGISTER' + REGISTER_LDAP = 'REGISTER_LDAP' + REQUEST_FILE = 'REQUEST_FILE' + RETRIVE_FILE = 'SEND_FILE' + UNREGISTER = 'UNREGISTER' + TASK_STATUS = 'TASK_STATUS' + RESPONSE_AGREEMENT = 'RESPONSE_AGREEMENT' + UPDATE_SCHEDULED_TASK = 'UPDATE_SCHEDULED_TASK' + REGISTRATION_RESPONSE ='REGISTRATION_RESPONSE' + LOGIN_RESPONSE = 'LOGIN_RESPONSE' diff --git a/usr/share/ahenk/base/model/message_factory.py b/usr/share/ahenk/base/model/message_factory.py new file mode 100644 index 0000000..17732d8 --- /dev/null +++ b/usr/share/ahenk/base/model/message_factory.py @@ -0,0 +1,17 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +from base.model.enum.message_type import MessageType + + +class MessageFactory(object): + def createMessage(self, type, message): + + if type == MessageType.TASK_RECEIVED: + return "Message receivden response" + elif type == MessageType.TASK_PROCESSING: + return "Message processing response" + else: + return None + + createMessage = staticmethod(createMessage) diff --git a/usr/share/ahenk/base/model/modes/__init__.py b/usr/share/ahenk/base/model/modes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/model/modes/init_mode.py b/usr/share/ahenk/base/model/modes/init_mode.py new file mode 100644 index 0000000..4af94e6 --- /dev/null +++ b/usr/share/ahenk/base/model/modes/init_mode.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + + +class InitMode(object): + def __init__(self): + pass + + @property + def obj_name(self): + return "INIT_MODE" diff --git a/usr/share/ahenk/base/model/modes/login_mode.py b/usr/share/ahenk/base/model/modes/login_mode.py new file mode 100644 index 0000000..f299abe --- /dev/null +++ b/usr/share/ahenk/base/model/modes/login_mode.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + + +class LoginMode(object): + def __init__(self, username): + self.username = username + + @property + def obj_name(self): + return "LOGIN_MODE" diff --git a/usr/share/ahenk/base/model/modes/logout_mode.py b/usr/share/ahenk/base/model/modes/logout_mode.py new file mode 100644 index 0000000..59959b4 --- /dev/null +++ b/usr/share/ahenk/base/model/modes/logout_mode.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + + +class LogoutMode(object): + def __init__(self, username): + self.username = username + + @property + def obj_name(self): + return "LOGOUT_MODE" diff --git a/usr/share/ahenk/base/model/modes/safe_mode.py b/usr/share/ahenk/base/model/modes/safe_mode.py new file mode 100644 index 0000000..ac3e9d1 --- /dev/null +++ b/usr/share/ahenk/base/model/modes/safe_mode.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + + +class SafeMode(object): + def __init__(self, username): + self.username = username + + @property + def obj_name(self): + return "SAFE_MODE" diff --git a/usr/share/ahenk/base/model/modes/shutdown_mode.py b/usr/share/ahenk/base/model/modes/shutdown_mode.py new file mode 100644 index 0000000..124a8b9 --- /dev/null +++ b/usr/share/ahenk/base/model/modes/shutdown_mode.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + + +class ShutdownMode(object): + def __init__(self): + pass + + @property + def obj_name(self): + return "SHUTDOWN_MODE" diff --git a/usr/share/ahenk/base/model/plugin.py b/usr/share/ahenk/base/model/plugin.py new file mode 100644 index 0000000..1b0ab85 --- /dev/null +++ b/usr/share/ahenk/base/model/plugin.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin +import json + + +class Plugin(object): + """docstring for Plugin""" + + def __init__(self, message): + self.plugin = message['plugin'] + + @property + def id(self): + return self.plugin['id'] + + @property + def name(self): + return self.plugin['name'] + + @property + def version(self): + return self.plugin['version'] + + @property + def description(self): + return self.plugin['description'] + + def to_string(self): + return str(self.plugin) + + def to_json(self): + return json.load(self.plugin) diff --git a/usr/share/ahenk/base/model/plugin_bean.py b/usr/share/ahenk/base/model/plugin_bean.py new file mode 100644 index 0000000..265b521 --- /dev/null +++ b/usr/share/ahenk/base/model/plugin_bean.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + + +class PluginBean(object): + """docstring for PluginBean""" + + def __init__(self, p_id=None, active=None, create_date=None, deleted=None, description=None, machine_oriented=None, modify_date=None, name=None, policy_plugin=None, task_plugin=None, user_oriented=None, version=None, x_based=None): + self.id = p_id + self.active = active + self.create_date = create_date + self.deleted = deleted + self.description = description + self.machine_oriented = machine_oriented + self.modify_date = modify_date + self.name = name + self.policy_plugin = policy_plugin + self.user_oriented = user_oriented + self.version = version + self.task_plugin = task_plugin + self.x_based = x_based + + def get_user_oriented(self): + return self.user_oriented + + def set_user_oriented(self, user_oriented): + self.user_oriented = user_oriented + + def get_policy_plugin(self): + return self.policy_plugin + + def set_policy_plugin(self, policy_plugin): + self.policy_plugin = policy_plugin + + def get_machine_oriented(self): + return self.machine_oriented + + def set_machine_oriented(self, machine_oriented): + self.machine_oriented = machine_oriented + + def get_modify_date(self): + return self.modify_date + + def set_modify_date(self, modify_date): + self.modify_date = modify_date + + def get_create_date(self): + return self.create_date + + def set_create_date(self, create_date): + self.create_date = create_date + + def get_deleted(self): + return self.deleted + + def set_deleted(self, deleted): + self.deleted = deleted + + def get_description(self): + return self.description + + def set_description(self, description): + self.description = description + + def get_active(self): + return self.active + + def set_active(self, active): + self.active = active + + def get_id(self): + return self.id + + def set_id(self, p_id): + self.id = p_id + + def get_name(self): + return self.name + + def set_name(self, name): + self.name = name + + def get_version(self): + return self.version + + def set_version(self, version): + self.version = version + + def get_x_based(self): + return self.x_based + + def set_x_based(self, x_based): + self.x_based = x_based + + def get_task_plugin(self): + return self.task_plugin + + def set_task_plugin(self, task_plugin): + self.task_plugin = task_plugin diff --git a/usr/share/ahenk/base/model/policy.py b/usr/share/ahenk/base/model/policy.py new file mode 100644 index 0000000..5bd2091 --- /dev/null +++ b/usr/share/ahenk/base/model/policy.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +# Author: Volkan Şahin +import json + +from base.model.profile import Profile + + +class Policy(object): + """docstring for Policy""" + + def __init__(self, message): + self.policy = message + + @property + def ahenk_policy_version(self): + return self.policy['agentPolicyVersion'] + + @property + def ahenk_profiles(self): + profiles = [] + for p in self.policy['agentPolicyProfiles']: + profiles.append(Profile(p)) + return profiles + + @property + def user_policy_version(self): + return self.policy['userPolicyVersion'] + + @property + def timestamp(self): + return self.policy['timestamp'] + + @property + def user_profiles(self): + profiles = [] + try: + for p in self.policy['userPolicyProfiles']: + profiles.append(Profile(p)) + return profiles + except Exception as e: + return None + + @property + def username(self): + return self.policy['username'] + + # TODO result mesajı dönerken döndür + @property + def ahenk_execution_id(self): + return self.policy['agentCommandExecutionId'] + + @property + def user_execution_id(self): + return self.policy['userCommandExecutionId'] + + def to_string(self): + return str(self.policy) + + def to_json(self): + return json.load(self.policy) + + def obj_name(self): + return "PROFILE" diff --git a/usr/share/ahenk/base/model/policy_bean.py b/usr/share/ahenk/base/model/policy_bean.py new file mode 100644 index 0000000..f8c9531 --- /dev/null +++ b/usr/share/ahenk/base/model/policy_bean.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + + +class PolicyBean(object): + """docstring for PolicyBean""" + + def __init__(self, ahenk_policy_version=None, user_policy_version=None, ahenk_profiles=None, user_profiles=None, + timestamp=None, username=None, agent_execution_id=None, user_execution_id=None, + agent_expiration_date=None, user_expiration_date=None): + self.ahenk_policy_version = ahenk_policy_version + self.user_policy_version = user_policy_version + self.ahenk_profiles = ahenk_profiles + self.user_profiles = user_profiles + self.timestamp = timestamp + self.username = username + self.agent_execution_id = agent_execution_id + self.user_execution_id = user_execution_id + self.agent_expiration_date = agent_expiration_date + self.user_expiration_date = user_expiration_date + + def get_ahenk_policy_version(self): + return self.ahenk_policy_version + + def set_ahenk_policy_version(self, ahenk_policy_version): + self.ahenk_policy_version = ahenk_policy_version + + def get_user_policy_version(self): + return self.user_policy_version + + def set_user_policy_version(self, user_policy_version): + self.user_policy_version = user_policy_version + + def get_ahenk_profiles(self): + return self.ahenk_profiles + + def set_ahenk_profiles(self, ahenk_profiles): + self.ahenk_profiles = ahenk_profiles + + def get_user_profiles(self): + return self.user_profiles + + def set_user_profiles(self, user_profiles): + self.user_profiles = user_profiles + + def get_timestamp(self): + return self.timestamp + + def set_timestamp(self, timestamp): + self.timestamp = timestamp + + def get_username(self): + return self.username + + def set_username(self, username): + self.username = username + + def get_agent_execution_id(self): + return self.agent_execution_id + + def set_agent_execution_id(self, agent_execution_id): + self.agent_execution_id = agent_execution_id + + def set_user_execution_id(self, user_execution_id): + self.user_execution_id = user_execution_id + + def get_user_execution_id(self): + return self.user_execution_id diff --git a/usr/share/ahenk/base/model/profile.py b/usr/share/ahenk/base/model/profile.py new file mode 100644 index 0000000..48dc19d --- /dev/null +++ b/usr/share/ahenk/base/model/profile.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin +import json + +from base.model.plugin import Plugin + + +class Profile(object): + """docstring for Profile""" + + def __init__(self, message): + self.profile = message + self.username = None + + @property + def id(self): + return self.profile['id'] + + @property + def create_date(self): + return self.profile['createDate'] + + @property + def modify_date(self): + return self.profile['modifyDate'] + + @property + def label(self): + return self.profile['label'] + + @property + def description(self): + return self.profile['description'] + + @property + def overridable(self): + return self.profile['overridable'] + + @property + def active(self): + return self.profile['active'] + + @property + def deleted(self): + return self.profile['deleted'] + + @property + def profile_data(self): + return self.profile['profileData'] + + @property + def plugin(self): + return Plugin(self.profile['plugin']) + + def to_string(self): + return str(self.profile) + + def to_json(self): + return json.load(self.profile) + + @property + def obj_name(self): + return "PROFILE" + + def get_username(self): + return self.username + + def set_username(self, username): + self.username = username diff --git a/usr/share/ahenk/base/model/profile_bean.py b/usr/share/ahenk/base/model/profile_bean.py new file mode 100644 index 0000000..8757380 --- /dev/null +++ b/usr/share/ahenk/base/model/profile_bean.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin +import json + +from base.model.plugin_bean import PluginBean + + +class ProfileBean(object): + """docstring for Profile""" + + def __init__(self, p_id=None, create_date=None, label=None, description=None, overridable=None, active=None, deleted=None, profile_data=None, modify_date=None, plugin=None, username=None): + self.id = p_id + self.create_date = create_date + self.modify_date = modify_date + self.label = label + self.description = description + self.overridable = overridable + self.active = active + self.deleted = deleted + self.profile_data = profile_data + self.plugin = plugin + self.username = username + + def get_id(self): + return self.id + + def set_id(self, p_id): + self.id = p_id + + def get_create_date(self): + return self.create_date + + def set_create_date(self, create_date): + self.create_date = create_date + + def get_modify_date(self): + return self.modify_date + + def set_modify_date(self, modify_date): + self.modify_date = modify_date + + def get_label(self): + return self.label + + def set_label(self, label): + self.label = label + + def get_description(self): + return self.modify_date + + def set_description(self, description): + self.description = description + + def get_overridable(self): + return self.overridable + + def set_overridable(self, overridable): + self.overridable = overridable + + def get_active(self): + return self.active + + def set_active(self, active): + self.active = active + + def get_deleted(self): + return self.deleted + + def set_deleted(self, deleted): + self.deleted = deleted + + def get_profile_data(self): + return self.profile_data + + def set_profile_data(self, profile_data): + self.profile_data = profile_data + + def get_plugin(self): + return self.plugin + + def set_plugin(self, plugin): + self.plugin = plugin + + def get_username(self): + return self.username + + def set_username(self, username): + self.username = username + + @property + def obj_name(self): + return "PROFILE" \ No newline at end of file diff --git a/usr/share/ahenk/base/model/response.py b/usr/share/ahenk/base/model/response.py new file mode 100644 index 0000000..131ca9f --- /dev/null +++ b/usr/share/ahenk/base/model/response.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import datetime + + +class Response(object): + """docstring for Plugin""" + + def __init__(self, type, id, code=None, message=None, data=None, content_type=None, execution_id=None, policy_version=None): + self.type = type + self.id = id + self.code = code + self.message = message + self.data = data + self.content_type = content_type + self.execution_id = execution_id + self.policy_version = policy_version + self.timestamp = str(datetime.datetime.now().strftime("%d-%m-%Y %I:%M")) + + def get_type(self): + return str(self.type) + + def set_type(self, type): + self.type = type + + def get_id(self): + return self.id + + def set_id(self, id): + self.id = id + + def get_code(self): + return str(self.code) + + def set_code(self, code): + self.code = code + + def get_message(self): + return self.message + + def set_message(self, message): + self.message = message + + def get_data(self): + return self.data + + def set_data(self, data): + self.data = data + + def get_content_type(self): + return self.content_type + + def set_content_type(self, content_type): + self.content_type = content_type + + def get_timestamp(self): + return self.timestamp + + def get_execution_id(self): + return str(self.execution_id) + + def set_execution_id(self, execution_id): + self.execution_id = execution_id + + def get_policy_version(self): + return self.policy_version + + def set_policy_version(self, policy_version): + self.policy_version = policy_version diff --git a/usr/share/ahenk/base/model/task.py b/usr/share/ahenk/base/model/task.py new file mode 100644 index 0000000..93778af --- /dev/null +++ b/usr/share/ahenk/base/model/task.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +# Author: Volkan Şahin +import json + +from base.model.plugin import Plugin + + +class Task(object): + """docstring for Task""" + + def __init__(self, message): + #TODO message must be json !!! otherwise we can not use task json methods! + #Remove if condition and change message param to json task type. + if message: + self.task = message['task'] + + @property + def id(self): + return self.task['id'] + + @property + def create_date(self): + return self.task['createDate'] + + @property + def modify_date(self): + return self.task['modifyDate'] + + @property + def command_cls_id(self): + return json.loads(str(self.task))['commandClsId'] + + @property + def parameter_map(self): + return self.task['parameterMap'] + + @property + def deleted(self): + return self.task['deleted'] + + @property + def plugin(self): + return Plugin(json.loads(str(self.task))) + + @property + def cron_str(self): + return '1 * * * *' #TODO update when task cron field added. + + def to_string(self): + return str(self.task) + + def to_json(self): + return json.dumps(self.task) + + def from_json(self,json_value): + self.task = json.load(json_value) + + @property + def obj_name(self): + return "TASK" + + def cols(self): + return ['id', 'create_date', 'modify_date', 'command_cls_id', 'parameter_map', 'deleted', 'plugin'] + + def values(self): + return [str(self.id), str(self.create_date), str(self.modify_date), str(self.command_cls_id), str(self.parameter_map), str(self.deleted), self.plugin.to_string()] diff --git a/usr/share/ahenk/base/model/task_bean.py b/usr/share/ahenk/base/model/task_bean.py new file mode 100644 index 0000000..7538112 --- /dev/null +++ b/usr/share/ahenk/base/model/task_bean.py @@ -0,0 +1,105 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +# Author: Volkan Şahin + + +class TaskBean(object): + """docstring for TaskBean""" + + def __init__(self, _id=None, create_date=None, modify_date=None, command_cls_id=None, parameter_map=None, deleted=None, plugin=None, cron_str=None, file_server=None): + self._id = _id + self.create_date = create_date + self.modify_date = modify_date + self.command_cls_id = command_cls_id + self.parameter_map = parameter_map + self.deleted = deleted + self.plugin = plugin + self.cron_str = cron_str + self.file_server = file_server + + def get_id(self): + return self._id + + def set_id(self, _id): + self._id = _id + + def get_create_date(self): + return self.create_date + + def set_create_date(self, create_date): + self.create_date = create_date + + def get_modify_date(self): + return self.modify_date + + def set_modify_date(self, modify_date): + self.modify_date = modify_date + + def get_command_cls_id(self): + return self.command_cls_id + + def set_command_cls_id(self, command_cls_id): + self.command_cls_id = command_cls_id + + def get_parameter_map(self): + return self.parameter_map + + def set_parameter_map(self, parameter_map): + self.parameter_map = parameter_map + + def get_deleted(self): + return self.deleted + + def set_deleted(self, deleted): + self.deleted = deleted + + def get_plugin(self): + return self.plugin + + def set_plugin(self, plugin): + self.plugin = plugin + + def get_cron_str(self): + return self.cron_str + + def set_cron_str(self, cron_str): + self.cron_str = cron_str + + def get_file_server(self): + return self.file_server + + def set_file_server(self, file_server): + self.file_server = file_server + + def to_json(self): + plugin_data = dict() + plugin_data['id'] = self.plugin.get_id() + plugin_data['name'] = self.plugin.get_name() + plugin_data['version'] = self.plugin.get_version() + plugin_data['description'] = self.plugin.get_description() + plugin_data['active'] = self.plugin.get_active() + plugin_data['deleted'] = self.plugin.get_deleted() + plugin_data['machineOriented'] = self.plugin.get_machine_oriented() + plugin_data['userOriented'] = self.plugin.get_user_oriented() + plugin_data['policyPlugin'] = self.plugin.get_policy_plugin() + plugin_data['taskPlugin'] = self.plugin.get_task_plugin() + plugin_data['xBased'] = self.plugin.get_x_based() + plugin_data['createDate'] = self.plugin.get_create_date() + plugin_data['modifyDate'] = self.plugin.get_modify_date() + + task_data = dict() + task_data['id'] = self._id + task_data['plugin'] = plugin_data + task_data['commandClsId'] = self.command_cls_id + task_data['parameterMap'] = self.parameter_map + task_data['deleted'] = self.deleted + task_data['cronExpression'] = self.cron_str + task_data['createDate'] = self.create_date + task_data['modifyDate'] = self.modify_date + task_data['fileServerConf'] = self.file_server + return task_data + + @property + def obj_name(self): + return "TASK" diff --git a/usr/share/ahenk/base/plugin/__init__.py b/usr/share/ahenk/base/plugin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/plugin/abstract_plugin.py b/usr/share/ahenk/base/plugin/abstract_plugin.py new file mode 100644 index 0000000..994063f --- /dev/null +++ b/usr/share/ahenk/base/plugin/abstract_plugin.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +# Author: Volkan Şahin + +from base.scope import Scope +from base.model.enum.content_type import ContentType +from base.model.enum.message_code import MessageCode +from base.system.system import System +from base.util.util import Util + + +class AbstractPlugin(Util, System): + """This is base class for plugins""" + + def __init__(self): + super(AbstractPlugin, self).__init__() + self.scope = Scope.get_instance() + + def handle_task(profile_data, context): + Scope.get_instance().get_logger().error('Handle function not found') + + def get_message_code(self): + return MessageCode + + def get_content_type(self): + return ContentType + + def get_logger(self): + try: + return Scope.get_instance().get_logger() + except Exception as e: + self.scope.get_logger().error( + 'A problem occurred while getting logger. Error Message: {0}'.format(str(e))) + return None + + +def configuration_manager(self): + try: + return self.scope.get_configuration_manager() + except Exception as e: + self.logger().error( + 'A problem occurred while getting configuration manager. Error Message: {0}'.format( + str(e))) + return None + + +def plugin_path(self): + return self.configuration_manager().get('PLUGIN', 'pluginfolderpath') diff --git a/usr/share/ahenk/base/plugin/file_handler.py b/usr/share/ahenk/base/plugin/file_handler.py new file mode 100644 index 0000000..2188e43 --- /dev/null +++ b/usr/share/ahenk/base/plugin/file_handler.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import os +import signal + +from watchdog.events import FileSystemEventHandler + +from base.command.command_manager import Commander +from base.system.system import System + + +class FileEventHandler(FileSystemEventHandler): + def __init__(self, plugin_path): + self.path = plugin_path + + def process(self, event): + + if event.src_path != self.path[:-1]: + if event.event_type == 'moved': + plu_path = event.dest_path + result = Commander().set_event([None, 'load', '-p', plu_path.replace(self.path, '')]) + if result is True and System.Ahenk.is_running() is True: + os.kill(int(System.Ahenk.get_pid_number()), signal.SIGALRM) + elif event.event_type == 'deleted': + result = Commander().set_event([None, 'remove', '-p', event.src_path.replace(self.path, '')]) + if result is True and System.Ahenk.is_running() is True: + os.kill(int(System.Ahenk.get_pid_number()), signal.SIGALRM) + + def on_any_event(self, event): + + if event.is_directory: + self.process(event) diff --git a/usr/share/ahenk/base/plugin/plugin.py b/usr/share/ahenk/base/plugin/plugin.py new file mode 100644 index 0000000..fc0f01e --- /dev/null +++ b/usr/share/ahenk/base/plugin/plugin.py @@ -0,0 +1,305 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +# Author: Volkan Şahin + +import json +import threading +import ast +from base.file.file_transfer_manager import FileTransferManager +from base.model.enum.content_type import ContentType +from base.model.enum.message_code import MessageCode +from base.model.enum.message_type import MessageType +from base.model.response import Response +from base.scope import Scope +from base.system.system import System +from base.util.util import Util + + +class Context(object): + def __init__(self): + self.data = dict() + self.scope = Scope().get_instance() + self.mail_send = False + self.mail_subject = '' + self.mail_content = '' + + def put(self, var_name, data): + self.data[var_name] = data + + def get(self, var_name): + return self.data[var_name] + + def get_username(self): + return self.data['username'] + + def empty_data(self): + self.data = dict() + + def create_response(self, code, message=None, data=None, content_type=None): + self.data['responseCode'] = code + self.data['responseMessage'] = message + self.data['responseData'] = data + self.data['contentType'] = content_type + + def fetch_file(self, remote_path, local_path=None, file_name=None): + success = None + try: + custom_map = self.get('parameterMap') + custom_map['path'] = remote_path + file_manager = FileTransferManager(self.get('protocol'), custom_map) + file_manager.transporter.connect() + success = file_manager.transporter.get_file(local_path, file_name) + file_manager.transporter.disconnect() + except Exception as e: + raise + + return success + + def is_mail_send(self): + return self.mail_send + + def set_mail_send(self, mail_send): + self.mail_send=mail_send + + def get_mail_subject(self): + return self.mail_subject + + def set_mail_subject(self, mail_subject): + self.mail_subject=mail_subject + + def get_mail_content(self): + return self.mail_content + + def set_mail_content(self, mail_content): + self.mail_content = mail_content + + +class Plugin(threading.Thread): + """ + This is a thread inherit class and have a queue. + Plugin class responsible for processing TASK or USER PLUGIN PROFILE. + """ + + def __init__(self, name, in_ueue): + threading.Thread.__init__(self) + self.name = name + self.in_queue = in_ueue + + scope = Scope.get_instance() + self.logger = scope.get_logger() + self.response_queue = scope.get_response_queue() + self.messaging = scope.get_message_manager() + self.db_service = scope.get_db_service() + + self.configurationManager = scope.get_configuration_manager() + self.is_user_notify_active = self.configurationManager.get('MACHINE', 'agreement') + + self.keep_run = True + self.context = Context() + + def run(self): + + while self.keep_run: + try: + try: + item_obj = self.in_queue.get(block=True) + obj_name = item_obj.obj_name + except Exception as e: + self.logger.error( + '[Plugin] A problem occurred while executing process. Error Message: {0}'.format(str(e))) + + if obj_name == "TASK": + self.logger.debug('[Plugin] Executing task') + self.context.put('task_id', item_obj.get_id()) + + if item_obj.get_file_server() is not None and item_obj.get_file_server() != 'null': + self.context.put('protocol', json.loads(item_obj.get_file_server())['protocol']) + self.context.put('parameterMap', json.loads(item_obj.get_file_server())['parameterMap']) + + if type(item_obj.get_parameter_map())==str: + task_data = json.loads(item_obj.get_parameter_map()) + elif type(item_obj.get_parameter_map())==dict: + task_data = item_obj.get_parameter_map() + + # check if mail send is actve or not and set mail params to context object.. plugins get mail params via context object + self.context.set_mail_send(task_data['mailSend'] if 'mailSend' in task_data else False) + self.context.set_mail_subject(task_data['mailSubject'] if 'mailSubject' in task_data else '') + self.context.set_mail_content(task_data['mailContent'] if 'mailContent' in task_data else '') + + self.logger.debug('[Plugin] Sending notify to user about task process') + + if System.Sessions.user_name() is not None and len(System.Sessions.user_name()) > 0 and self.is_user_notify_active == 1: + for user in System.Sessions.user_name(): + Util.send_notify("Lider Ahenk", + "{0} eklentisi şu anda bir görev çalıştırıyor.".format(self.getName()), + System.Sessions.display(user), + user) + + self.context.put('taskData', task_data) + self.context.put('taskId', item_obj.get_id()) + + self.logger.debug('[Plugin] Handling task') + Scope.get_instance().get_plugin_manager().find_command(self.getName(), + item_obj.get_plugin().get_version(), + item_obj.get_command_cls_id().lower()).handle_task(ast.literal_eval(str(task_data)), self.context) + + + if self.context.data is not None and self.context.get('responseCode') is not None: + self.logger.debug('[Plugin] Creating response') + response = Response(type=MessageType.TASK_STATUS.value, id=item_obj.get_id(), + code=self.context.get('responseCode'), + message=self.context.get('responseMessage'), + data=self.context.get('responseData'), + content_type=self.context.get('contentType')) + + if response.get_data() and response.get_content_type() != ContentType.APPLICATION_JSON.value: + success = False + try: + file_manager = FileTransferManager(json.loads(item_obj.get_file_server())['protocol'], + json.loads(item_obj.get_file_server())[ + 'parameterMap']) + file_manager.transporter.connect() + md5 = str(json.loads(response.get_data())['md5']) + success = file_manager.transporter.send_file(System.Ahenk.received_dir_path() + md5, + md5) + file_manager.transporter.disconnect() + except Exception as e: + self.logger.error( + '[Plugin] A problem occurred while file transferring. Error Message :{0}'.format( + str(e))) + + self.logger.debug('[Plugin] Sending response') + + message = self.messaging.task_status_msg(response) + if success is False: + response = Response(type=MessageType.TASK_STATUS.value, id=item_obj.get_id(), + code=MessageCode.TASK_ERROR.value, + message='Task processed successfully but file transfer not completed. Check defined server conf') + message = self.messaging.task_status_msg(response) + + Scope.get_instance().get_messenger().send_direct_message(message) + + else: + self.logger.debug('[Plugin] Sending task response') + Scope.get_instance().get_messenger().send_direct_message( + self.messaging.task_status_msg(response)) + + else: + self.logger.error( + '[Plugin] There is no Response. Plugin must create response after run a task!') + + elif obj_name == "PROFILE": + + self.logger.debug('[Plugin] Executing profile') + profile_data = item_obj.get_profile_data() + self.context.put('username', item_obj.get_username()) + + json_profile_data=json.loads(profile_data) + self.context.set_mail_send(json_profile_data['mailSend'] if 'mailSend' in json_profile_data else False) + self.context.set_mail_subject(json_profile_data['mailSubject'] if 'mailSubject' in json_profile_data else '') + self.context.set_mail_content(json_profile_data['mailContent'] if 'mailContent' in json_profile_data else '') + + + execution_id = self.get_execution_id(item_obj.get_id()) + policy_ver = self.get_policy_version(item_obj.get_id()) + + self.context.put('policy_version', policy_ver) + self.context.put('execution_id', execution_id) + + # if item_obj.get_file_server() is not None and item_obj.get_file_server() !='null': + # self.context.put('protocol', json.loads(item_obj.get_file_server())['protocol']) + # self.context.put('parameterMap', json.loads(item_obj.get_file_server())['parameterMap']) + + self.logger.debug('[Plugin] Sending notify to user about profile process') + + Util.send_notify("Lider Ahenk", + "{0} eklentisi şu anda bir profil çalıştırıyor.".format(self.getName()), + System.Sessions.display(item_obj.get_username()), + item_obj.get_username()) + self.logger.debug('[Plugin] Handling profile') + Scope.get_instance().get_plugin_manager().find_policy_module(item_obj.get_plugin().get_name()).handle_policy(profile_data, self.context) + + if self.context.data is not None and self.context.get('responseCode') is not None: + self.logger.debug('[Plugin] Creating response') + response = Response(type=MessageType.POLICY_STATUS.value, id=item_obj.get_id(), + code=self.context.get('responseCode'), + message=self.context.get('responseMessage'), + data=self.context.get('responseData'), + content_type=self.context.get('contentType'), execution_id=execution_id, + policy_version=policy_ver) + + if response.get_data() and response.get_content_type() != ContentType.APPLICATION_JSON.value: + success = False + try: + file_manager = FileTransferManager(json.loads(item_obj.get_file_server())['protocol'], + json.loads(item_obj.get_file_server())[ + 'parameterMap']) + file_manager.transporter.connect() + md5 = str(json.loads(response.get_data())['md5']) + success = file_manager.transporter.send_file(System.Ahenk.received_dir_path() + md5, + md5) + file_manager.transporter.disconnect() + except Exception as e: + self.logger.error( + '[Plugin] A problem occurred while file transferring. Error Message :{0}'.format( + str(e))) + + self.logger.debug('[Plugin] Sending response') + + message = self.messaging.task_status_msg(response) + if success is False: + response = Response(type=MessageType.POLICY_STATUS.value, id=item_obj.get_id(), + code=MessageCode.POLICY_ERROR.value, + message='Policy processed successfully but file transfer not completed. Check defined server conf') + message = self.messaging.task_status_msg(response) + Scope.get_instance().get_messenger().send_direct_message(message) + else: + self.logger.debug('[Plugin] Sending profile response') + Scope.get_instance().get_messenger().send_direct_message( + self.messaging.policy_status_msg(response)) + else: + self.logger.error( + '[Plugin] There is no Response. Plugin must create response after run a policy!') + elif 'MODE' in obj_name: + module = Scope.get_instance().get_plugin_manager().find_module(obj_name, self.name) + if module is not None: + if item_obj.obj_name in ('LOGIN_MODE', 'LOGOUT_MODE', 'SAFE_MODE'): + self.context.put('username', item_obj.username) + try: + self.logger.debug( + '[Plugin] {0} is running on {1} plugin'.format(str(item_obj.obj_name), str(self.name))) + module.handle_mode(self.context) + except Exception as e: + self.logger.error( + '[Plugin] A problem occurred while running {0} on {1} plugin. Error Message: {2}'.format( + str(obj_name), str(self.name), str(e))) + + if item_obj.obj_name is 'SHUTDOWN_MODE': + self.logger.debug('[Plugin] {0} plugin is stopping...'.format(str(self.name))) + self.keep_run = False + else: + self.logger.warning("[Plugin] Not supported object type: {0}".format(str(obj_name))) + + self.context.empty_data() + except Exception as e: + self.logger.error("[Plugin] Plugin running exception. Exception Message: {0} ".format(str(e))) + + def get_execution_id(self, profile_id): + try: + return self.db_service.select_one_result('policy', 'execution_id', ' id={0}'.format(profile_id)) + except Exception as e: + self.logger.error( + "[Plugin] A problem occurred while getting execution id. Exception Message: {0} ".format(str(e))) + return None + + def get_policy_version(self, profile_id): + try: + return self.db_service.select_one_result('policy', 'version', ' id={0}'.format(profile_id)) + except Exception as e: + self.logger.error( + "[Plugin] A problem occurred while getting policy version . Exception Message: {0} ".format(str(e))) + return None + + def getName(self): + return self.name diff --git a/usr/share/ahenk/base/plugin/plugin_install_listener.py b/usr/share/ahenk/base/plugin/plugin_install_listener.py new file mode 100644 index 0000000..27a0763 --- /dev/null +++ b/usr/share/ahenk/base/plugin/plugin_install_listener.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import threading +import time + +from watchdog.observers import Observer + +from base.plugin.file_handler import FileEventHandler + + +class PluginInstallListener(threading.Thread): + def __init__(self, plugin_path): + threading.Thread.__init__(self) + self.path = plugin_path + + def run(self): + observer = Observer() + event_handler = FileEventHandler(self.path) + observer.schedule(event_handler, self.path, recursive=False) + observer.start() + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + observer.stop() + observer.join() diff --git a/usr/share/ahenk/base/plugin/plugin_manager.py b/usr/share/ahenk/base/plugin/plugin_manager.py new file mode 100644 index 0000000..c2dc6d2 --- /dev/null +++ b/usr/share/ahenk/base/plugin/plugin_manager.py @@ -0,0 +1,311 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +# Author: Volkan Şahin +import imp +import os + +from base.scope import Scope +from base.model.plugin_bean import PluginBean +from base.model.modes.init_mode import InitMode +from base.model.modes.login_mode import LoginMode +from base.model.modes.logout_mode import LogoutMode +from base.model.modes.safe_mode import SafeMode +from base.model.modes.shutdown_mode import ShutdownMode +from base.plugin.plugin import Plugin +from base.plugin.plugin_queue import PluginQueue +from base.plugin.plugin_install_listener import PluginInstallListener +from base.system.system import System + + +# TODO create base abstract class +class PluginManager(object): + """docstring for PluginManager""" + + def __init__(self): + super(PluginManager, self).__init__() + self.scope = Scope.get_instance() + self.config_manager = self.scope.get_configuration_manager() + self.db_service = self.scope.get_db_service() + self.message_manager = self.scope.get_message_manager() + self.logger = self.scope.get_logger() + + self.plugins = [] + self.plugin_queue_dict = dict() + + # self.listener = \ + self.install_listener() + self.delayed_profiles = dict() + self.delayed_tasks = dict() + + # TODO version? + def load_plugins(self): + self.logger.info('Loading plugins...') + self.plugins = [] + self.logger.debug('Lookup for possible plugins...') + try: + possible_plugins = os.listdir(self.config_manager.get("PLUGIN", "pluginFolderPath")) + self.logger.debug('Possible plugins: {0} '.format(str(possible_plugins))) + for plugin_name in possible_plugins: + try: + self.load_single_plugin(plugin_name) + except Exception as e: + self.logger.error( + 'Exception occurred while loading plugin ! Plugin name : {0}.' + ' Error Message: {1}'.format(str(plugin_name), str(e))) + self.logger.info('Loaded plugins successfully.') + except Exception as e: + self.logger.warning('Plugin folder path not found. Error Message: {0}'.format(str(e))) + + def load_single_plugin(self, plugin_name): + # TODO check already loaded plugin + location = os.path.join(self.config_manager.get("PLUGIN", "pluginFolderPath"), plugin_name) + if not os.path.isdir(location) or not self.config_manager.get("PLUGIN", "mainModuleName") + ".py" in os.listdir( + location): + self.logger.debug( + 'It is not a plugin location ! There is no main module - {0}'.format(str(location))) + else: + if self.is_plugin_loaded(plugin_name): + self.logger.debug( + '{0} plugin was already loaded. Reloading {0} plugin'.format(plugin_name)) + # self.reload_single_plugin(plugin_name) + else: + self.plugin_queue_dict[plugin_name] = PluginQueue() + plugin = Plugin(plugin_name, self.plugin_queue_dict[plugin_name]) + plugin.setDaemon(True) + plugin.start() + self.plugins.append(plugin) + self.logger.debug('New plugin was loaded. Plugin Name: {0}'.format(plugin_name)) + + # active init mode + mode = InitMode() + self.plugin_queue_dict[plugin_name].put(mode, 1) + + if plugin_name in self.delayed_profiles: + self.plugin_queue_dict[plugin_name].put(self.delayed_profiles[plugin_name], 1) + del self.delayed_profiles[plugin_name] + self.logger.debug('Delayed profile was found for this plugin. It will be run.') + if plugin_name in self.delayed_tasks: + self.plugin_queue_dict[plugin_name].put(self.delayed_tasks[plugin_name], 1) + del self.delayed_tasks[plugin_name] + self.logger.debug('Delayed task was found for this plugin. It will be run.') + + def reload_plugins(self): + try: + self.logger.info('Reloading plugins...') + for p_queue in self.plugin_queue_dict: + self.plugin_queue_dict[p_queue].put(ShutdownMode(), 1) + self.plugins = [] + self.load_plugins() + self.logger.info('Plugin reloaded successfully.') + except Exception as e: + self.logger.error('Exception occurred when reloading plugins ' + str(e)) + + def reload_single_plugin(self, plugin_name): + try: + self.logger.info('{0} plugin is reloading'.format(plugin_name)) + self.logger.debug('{0} plugin is killing (in reloading action)'.format(plugin_name)) + self.remove_single_plugin(plugin_name) + self.logger.debug('{0} plugin is loading (in reloading action)'.format(plugin_name)) + self.load_single_plugin(plugin_name) + except Exception as e: + self.logger.error( + 'A problem occurred while reloading {0} plugin. Error Message: {1}'.format(plugin_name, + str(e))) + + def remove_plugins(self): + try: + self.logger.debug('Removing all plugins...') + for p_queue in self.plugin_queue_dict: + self.plugin_queue_dict[p_queue].put(ShutdownMode(), 1) + self.plugins = [] + self.plugin_queue_dict = dict() + self.logger.debug('All plugins were removed successfully.') + except Exception as e: + self.logger.error( + 'A problem occurred while removing plugins. Error Message :{0}.'.format(str(e))) + + def remove_single_plugin(self, plugin_name): + try: + self.logger.debug('Trying to remove {0} plugin...'.format(plugin_name)) + if self.is_plugin_loaded(plugin_name): + self.logger.debug('{0} plugin is killing...'.format(plugin_name)) + self.plugin_queue_dict[plugin_name].put(ShutdownMode(), 1) + del self.plugin_queue_dict[plugin_name] + + for plugin in self.plugins: + if plugin.name == plugin_name: + self.plugins.remove(plugin) + self.logger.debug('{0} plugin was removed.'.format(plugin_name)) + else: + self.logger.warning('{0} plugin not found.'.format(plugin_name)) + except Exception as e: + self.logger.error( + 'A problem occurred while removing {0} plugin. Error Message :{1}.'.format(plugin_name, + str(e))) + + def find_command(self, plugin_name, version, command_id): + location = os.path.join(self.config_manager.get("PLUGIN", "pluginFolderPath"), plugin_name) + if os.path.isdir(location) and command_id + ".py" in os.listdir(location): + info = imp.find_module(command_id, [location]) + return imp.load_module(command_id, *info) + else: + self.logger.warning('Command id -' + command_id + ' - not found') + return None + + def process_task(self, task): + + ## + scope = Scope().get_instance() + self.messenger = scope.get_messenger() + ## + + try: + plugin_name = task.get_plugin().get_name().lower() + plugin_ver = task.get_plugin().get_version() + + if self.does_plugin_exist(plugin_name, plugin_ver) and plugin_name in self.plugin_queue_dict: + self.plugin_queue_dict[plugin_name].put(task, 1) + else: + self.logger.warning( + '{0} plugin not found. Task was delayed. Ahenk will request plugin from Lider if distribution available'.format( + plugin_name, plugin_ver)) + self.delayed_tasks[plugin_name] = task + msg = self.message_manager.missing_plugin_message(PluginBean(name=plugin_name, version=plugin_ver)) + self.messenger.send_direct_message(msg) + except Exception as e: + self.logger.error( + 'Exception occurred while processing task. Error Message: {0}'.format(str(e))) + + def find_policy_module(self, plugin_name): + location = os.path.join(self.config_manager.get("PLUGIN", "pluginFolderPath"), plugin_name) + if os.path.isdir(location) and "policy.py" in os.listdir(location): + info = imp.find_module("policy", [location]) + return imp.load_module("policy", *info) + else: + self.logger.warning('policy.py not found Plugin Name : ' + str(plugin_name)) + return None + + def process_policy(self, policy): + + self.logger.info('Processing policies...') + username = policy.username + ahenk_profiles = policy.ahenk_profiles + user_profiles = policy.user_profiles + + if ahenk_profiles is not None and len(ahenk_profiles) > 0: + self.logger.info('Working on Ahenk profiles...') + for agent_profile in ahenk_profiles: + same_plugin_profile = None + + if user_profiles is not None: + for usr_profile in user_profiles: + if usr_profile.plugin.name == agent_profile.plugin.name: + same_plugin_profile = usr_profile + + if same_plugin_profile is not None: + if agent_profile.overridable.lower() == 'true': + self.logger.debug( + 'Agent profile of {0} plugin will not executed because of ' + 'profile override rules.'.format(agent_profile.plugin.name)) + continue + else: + self.logger.warning( + 'User profile of {0} plugin will not executed because of ' + 'profile override rules.'.format(agent_profile.plugin.name)) + user_profiles.remove(same_plugin_profile) + + agent_profile.set_username(None) + self.process_profile(agent_profile) + + if user_profiles is not None and len(user_profiles) > 0: + self.logger.info('Working on User profiles...') + for user_profile in user_profiles: + user_profile.set_username(username) + self.process_profile(user_profile) + + def process_profile(self, profile): + + try: + plugin = profile.get_plugin() + plugin_name = plugin.get_name() + plugin_ver = plugin.get_version() + if self.does_plugin_exist(plugin_name, plugin_ver) and plugin_name in self.plugin_queue_dict: + self.plugin_queue_dict[plugin_name].put(profile, 1) + else: + self.logger.warning( + '{0} plugin {1} version not found. Profile was delayed. Ahenk will request plugin from Lider if distribution available'.format( + plugin_name, plugin_ver)) + self.delayed_profiles[plugin_name] = profile + msg = self.message_manager.missing_plugin_message(PluginBean(name=plugin_name, version=plugin_ver)) + self.scope.get_messenger().send_direct_message(msg) + except Exception as e: + self.logger.error( + 'Exception occurred while processing profile. Error Message: {0}'.format(str(e))) + + def does_plugin_exist(self, name, version): + location = os.path.join(self.config_manager.get("PLUGIN", "pluginFolderPath"), name) + main = self.config_manager.get("PLUGIN", "mainModuleName") + if os.path.isdir(location) and main + ".py" in os.listdir(location): + info = imp.find_module(main, [location]) + main_py = imp.load_module(main, *info) + if main_py.info() is None or main_py.info()['version'] == version: + return True + return False + + def process_mode(self, mode_type, username=None): + mode = None + if mode_type == 'init': + mode = InitMode() + elif mode_type == 'shutdown': + mode = ShutdownMode() + elif mode_type == 'login': + mode = LoginMode(username) + elif mode_type == 'logout': + mode = LogoutMode(username) + elif mode_type == 'safe': + mode = SafeMode(username) + else: + self.logger.error('Unknown mode type: {0}'.format(mode_type)) + + if mode is not None: + self.logger.info('{0} mode is running'.format(mode_type)) + for plugin_name in self.plugin_queue_dict: + try: + self.plugin_queue_dict[plugin_name].put(mode, 1) + except Exception as e: + self.logger.error( + 'Exception occurred while switching safe mode. Error Message : {0}'.format( + str(e))) + + def find_module(self, mode, plugin_name): + mode = mode.lower().replace('_mode', '') + location = os.path.join(self.config_manager.get("PLUGIN", "pluginFolderPath"), plugin_name) + + if os.path.isdir(location) and (mode + ".py") in os.listdir(location): + info = imp.find_module(mode, [location]) + return imp.load_module(mode, *info) + else: + self.logger.warning('{0} not found in {1} plugin'.format((mode + '.py'), plugin_name)) + return None + + def install_listener(self): + listener = PluginInstallListener(System.Ahenk.plugins_path()) + listener.setDaemon(True) + listener.start() + + def is_plugin_loaded(self, plugin_name): + try: + if self.plugin_queue_dict[plugin_name] is not None: + return True + else: + return False + except Exception as e: + return False + + def checkCommandExist(self, pluginName, commandId): + # Not implemented yet + pass + + def printQueueSize(self): + print("size " + str(len(self.plugin_queue_dict))) diff --git a/usr/share/ahenk/base/plugin/plugin_manager_factory.py b/usr/share/ahenk/base/plugin/plugin_manager_factory.py new file mode 100644 index 0000000..5e77b35 --- /dev/null +++ b/usr/share/ahenk/base/plugin/plugin_manager_factory.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN + +from base.plugin.plugin_manager import PluginManager + + +class PluginManagerFactory(object): + def get_instance(): + return PluginManager() + + get_instance = staticmethod(get_instance) diff --git a/usr/share/ahenk/base/plugin/plugin_queue.py b/usr/share/ahenk/base/plugin/plugin_queue.py new file mode 100644 index 0000000..d7a42e9 --- /dev/null +++ b/usr/share/ahenk/base/plugin/plugin_queue.py @@ -0,0 +1,11 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN + +from queue import Queue + + +class PluginQueue(Queue): + def __contains__(self, item): + with self.mutex: + return item in self.queue diff --git a/usr/share/ahenk/base/registration/__init__.py b/usr/share/ahenk/base/registration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/registration/config-files/ldap b/usr/share/ahenk/base/registration/config-files/ldap new file mode 100644 index 0000000..f3ec76b --- /dev/null +++ b/usr/share/ahenk/base/registration/config-files/ldap @@ -0,0 +1,20 @@ +Name: Ahenk LDAP Authentication +Default: yes +Priority: 128 +Auth-Type: Primary +Auth-Initial: + [success=end authinfo_unavail=ignore default=ignore] pam_ldap.so +Auth: + [success=end authinfo_unavail=ignore default=ignore] pam_ldap.so use_first_pass +Account-Type: Primary +Account: + [success=end new_authtok_reqd=done authinfo_unavail=1 default=ignore] pam_ldap.so +Password-Type: Primary +Password-Initial: + [success=end user_unknown=ignore default=die] pam_ldap.so +Password: + [success=end user_unknown=ignore default=die] pam_ldap.so try_first_pass +Session-Type: Additional +Session: + optional pam_ldap.so + required pam_mkhomedir.so skel=/etc/skel umask=066 \ No newline at end of file diff --git a/usr/share/ahenk/base/registration/config-files/pam_script b/usr/share/ahenk/base/registration/config-files/pam_script new file mode 100644 index 0000000..bd149bd --- /dev/null +++ b/usr/share/ahenk/base/registration/config-files/pam_script @@ -0,0 +1,12 @@ +Name: Ahenk PAM scripts +Default: yes +Priority: 257 +Auth-Type: Primary +Auth: + optional pam_script.so +Account-Type: Primary +Account: + optional pam_script.so +Session-Type: Additional +Session: + optional pam_script.so diff --git a/usr/share/ahenk/base/registration/config-files/sssd.conf b/usr/share/ahenk/base/registration/config-files/sssd.conf new file mode 100644 index 0000000..d872d0f --- /dev/null +++ b/usr/share/ahenk/base/registration/config-files/sssd.conf @@ -0,0 +1,42 @@ +[sssd] +config_file_version = 2 +services = nss, pam, sudo +domains = LDAP + +[nss] + +[sudo] + +[pam] +pam_verbosity=2 +pam_account_locked_message = Hesap Kilitli +offline_credentials_expiration = 30 + +[domain/LDAP] +debug_level = 9 +id_provider = ldap +auth_provider = ldap +access_provider = ldap +#ldap_access_filter = (employeeType=admin) +ldap_access_order = ppolicy +pam_verbosity=2 +###ldap_pwdlockout_dn### +ldap_schema = rfc2307 +###ldap_uri### +###ldap_default_bind_dn### +###ldap_default_authtok### +ldap_default_authtok_type = password +###ldap_search_base### +###ldap_user_search_base### +###ldap_group_search_base### +ldap_user_object_class = posixAccount +ldap_user_gecos = cn +ldap_tls_reqcert = never +ldap_auth_disable_tls_never_use_in_production = true +override_shell = /bin/bash +enumerate = true +cache_credentials = true +sudo_provider = ldap +ldap_sudo_search_base = ou=Roles,dc=liderahenk,dc=org +ldap_sudo_full_refresh_interval=86400 +ldap_sudo_smart_refresh_interval=3600 \ No newline at end of file diff --git a/usr/share/ahenk/base/registration/execute_cancel_ldap_login.py b/usr/share/ahenk/base/registration/execute_cancel_ldap_login.py new file mode 100644 index 0000000..5b91b7e --- /dev/null +++ b/usr/share/ahenk/base/registration/execute_cancel_ldap_login.py @@ -0,0 +1,101 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +from base.scope import Scope +from base.util.util import Util +import re + + +class ExecuteCancelLDAPLogin: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.util = Util() + + def cancel(self): + self.logger.info('Purge ldap packages') + self.util.execute("apt-get install sudo -y") + self.util.execute("apt purge libpam-ldap libnss-ldap ldap-utils sudo-ldap nss-updatedb libnss-db libpam-ccreds -y") + self.util.execute("apt autoremove -y") + + self.logger.info('purging successfull') + + # pattern for clearing file data from spaces, tabs and newlines + pattern = re.compile(r'\s+') + + ldap_back_up_file_path = "/usr/share/ahenk/pam_scripts_original/ldap" + ldap_original_file_path = "/usr/share/pam-configs/ldap" + + pam_script_back_up_file_path = "/usr/share/ahenk/pam_scripts_original/pam_script" + pam_script_original_file_path = "/usr/share/pam-configs/pam_script" + + if self.util.is_exist(ldap_back_up_file_path): + self.logger.info("Replacing {0} with {1}".format(ldap_original_file_path, ldap_back_up_file_path)) + self.util.copy_file(ldap_back_up_file_path, ldap_original_file_path) + self.logger.info("Deleting {0}".format(ldap_back_up_file_path)) + self.util.delete_file(ldap_back_up_file_path) + + if self.util.is_exist(pam_script_back_up_file_path): + self.logger.info( + "Replacing {0} with {1}".format(pam_script_original_file_path, pam_script_back_up_file_path)) + self.util.copy_file(pam_script_back_up_file_path, pam_script_original_file_path) + self.logger.info("Deleting {0}".format(pam_script_back_up_file_path)) + self.util.delete_file(pam_script_back_up_file_path) + + (result_code, p_out, p_err) = self.util.execute("DEBIAN_FRONTEND=noninteractive pam-auth-update --package") + if result_code == 0: + self.logger.info("'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' has run successfully") + else: + self.logger.error( + "'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' could not run successfully: " + p_err) + + # Configure nsswitch.conf + file_ns_switch = open("/etc/nsswitch.conf", 'r') + file_data = file_ns_switch.read() + + # cleared file data from spaces, tabs and newlines + text = pattern.sub('', file_data) + + did_configuration_change = False + if "passwd:compatldap[NOTFOUND=return]db" in text: + file_data = file_data.replace("passwd: compat ldap [NOTFOUND=return] db", "passwd: compat") + did_configuration_change = True + + if "group:compatldap[NOTFOUND=return]db" in text: + file_data = file_data.replace("group: compat ldap [NOTFOUND=return] db", "group: compat") + did_configuration_change = True + + if "shadow:compatldap" in text: + file_data = file_data.replace("shadow: compat ldap", "shadow: compat") + did_configuration_change = True + + if "#gshadow:files" in text: + file_data = file_data.replace("#gshadow: files", "gshadow: files") + did_configuration_change = True + + if did_configuration_change: + self.logger.info("nsswitch.conf configuration has been configured") + else: + self.logger.info("nsswitch.conf has already been configured") + + file_ns_switch.close() + file_ns_switch = open("/etc/nsswitch.conf", 'w') + file_ns_switch.write(file_data) + file_ns_switch.close() + + # Configure ldap-cache + nss_update_cron_job_file_path = "/etc/cron.daily/nss-updatedb" + if self.util.is_exist(nss_update_cron_job_file_path): + self.util.delete_file(nss_update_cron_job_file_path) + self.logger.info("{0} is deleted.".format(nss_update_cron_job_file_path)) + + # Configure lightdm.service + pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" + if self.util.is_exist(pardus_xfce_path): + self.logger.info("99-pardus-xfce.conf exists. Deleting file.") + self.util.delete_file(pardus_xfce_path) + + self.util.execute("systemctl restart nscd.service") + self.logger.info("Operation finished") + diff --git a/usr/share/ahenk/base/registration/execute_cancel_sssd_authentication.py b/usr/share/ahenk/base/registration/execute_cancel_sssd_authentication.py new file mode 100644 index 0000000..52b6e88 --- /dev/null +++ b/usr/share/ahenk/base/registration/execute_cancel_sssd_authentication.py @@ -0,0 +1,91 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +from base.scope import Scope +from base.util.util import Util +import re + + +class ExecuteCancelSSSDAuthentication: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.util = Util() + + def cancel(self): + self.util.execute("apt purge libpam-sss sssd-common -y") + self.util.execute("apt autoremove -y") + + if self.util.is_exist("/etc/sssd"): + self.util.delete_folder("/etc/sssd") + + # pattern for clearing file data from spaces, tabs and newlines + pattern = re.compile(r'\s+') + + # Configure nsswitch.conf + file_ns_switch = open("/etc/nsswitch.conf", 'r') + file_data = file_ns_switch.read() + + # cleared file data from spaces, tabs and newlines + text = pattern.sub('', file_data) + + did_configuration_change = False + if "passwd:compatsss" in text: + file_data = file_data.replace("passwd: compat sss", "passwd: compat") + did_configuration_change = True + + if "group:compatsss" in text: + file_data = file_data.replace("group: compat sss", "group: compat") + did_configuration_change = True + + if "shadow:compatsss" in text: + file_data = file_data.replace("shadow: compat sss", "shadow: compat") + did_configuration_change = True + + if "services:dbfilessss" in text: + file_data = file_data.replace("services: db files sss", "services: db files") + did_configuration_change = True + + if "netgroup:nissss" in text: + file_data = file_data.replace("netgroup: nis sss", "netgroup: nis") + did_configuration_change = True + + if "sudoers:filessss" in text: + file_data = file_data.replace("sudoers: files sss", "") + did_configuration_change = True + + if did_configuration_change: + self.logger.info("nsswitch.conf configuration has been configured") + else: + self.logger.info("nsswitch.conf has already been configured") + + file_ns_switch.close() + file_ns_switch = open("/etc/nsswitch.conf", 'w') + file_ns_switch.write(file_data) + file_ns_switch.close() + + common_session_conf_path = "/etc/pam.d/common-session" + + # configure common-session for creating home directories for ldap users + file_common_session = open(common_session_conf_path, 'r') + file_data = file_common_session.read() + + if "session optional pam_mkhomedir.so skel=/etc/skel umask=077" in file_data: + file_data = file_data.replace("session optional pam_mkhomedir.so skel=/etc/skel umask=077", "") + self.logger.info("common-session is configured") + + file_common_session.close() + file_common_session = open(common_session_conf_path, 'w') + file_common_session.write(file_data) + file_common_session.close() + + # Configure lightdm.service + pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" + if self.util.is_exist(pardus_xfce_path): + self.logger.info("99-pardus-xfce.conf exists. Deleting file.") + self.util.delete_file(pardus_xfce_path) + self.util.execute("systemctl restart nscd.service") + + self.logger.info("LDAP Login iptal etme işlemi başarı ile sağlandı.") + diff --git a/usr/share/ahenk/base/registration/execute_ldap_login.py b/usr/share/ahenk/base/registration/execute_ldap_login.py new file mode 100644 index 0000000..2e189fe --- /dev/null +++ b/usr/share/ahenk/base/registration/execute_ldap_login.py @@ -0,0 +1,232 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +from base.scope import Scope +from base.util.util import Util +import re + + +class ExecuteLDAPLogin: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.util = Util() + + def login(self, server_address, dn, version, admin_dn, admin_password): + try: + self.logger.info("----------------> server_address: " + server_address) + self.logger.info("----------------> dn: " + dn) + self.logger.info("----------------> version: " + version) + self.logger.info("----------------> admin_dn: " + admin_dn) + self.logger.info("----------------> admin_password: " + admin_password) + #(result_code, p_out, p_err) = self.util.execute("/bin/bash /usr/share/ahenk/base/registration/scripts/test.sh") + (result_code, p_out, p_err) = self.util.execute("/bin/bash /usr/share/ahenk/base/registration/scripts/ldap-login.sh {0} {1} {2} {3} {4}".format(server_address, "\'" + dn + "\'", "\'" + admin_dn + "\'", "\'" + admin_password + "\'", version)) + if result_code == 0: + self.logger.info("Script has run successfully") + else: + self.logger.error("Script could not run successfully: " + p_err) + + # pattern for clearing file data from spaces, tabs and newlines + pattern = re.compile(r'\s+') + + pam_scripts_original_directory_path = "/usr/share/ahenk/pam_scripts_original" + + ldap_back_up_file_path = "/usr/share/ahenk/pam_scripts_original/ldap" + ldap_original_file_path = "/usr/share/pam-configs/ldap" + ldap_configured_file_path = "/usr/share/ahenk/plugins/ldap-login/config-files/ldap" + + pam_script_back_up_file_path = "/usr/share/ahenk/pam_scripts_original/pam_script" + pam_script_original_file_path = "/usr/share/pam-configs/pam_script" + pam_script_configured_file_path = "/usr/share/ahenk/plugins/ldap-login/config-files/pam_script" + + # create pam_scripts_original directory if not exists + if not self.util.is_exist(pam_scripts_original_directory_path): + self.logger.info("Creating {0} directory.".format(pam_scripts_original_directory_path)) + self.util.create_directory(pam_scripts_original_directory_path) + + if self.util.is_exist(ldap_back_up_file_path): + self.logger.info("Changing {0} with {1}.".format(ldap_original_file_path, ldap_configured_file_path)) + self.util.copy_file(ldap_configured_file_path, ldap_original_file_path) + else: + self.logger.info("Backing up {0}".format(ldap_original_file_path)) + self.util.copy_file(ldap_original_file_path, ldap_back_up_file_path) + self.logger.info( + "{0} file is replaced with {1}.".format(ldap_original_file_path, ldap_configured_file_path)) + self.util.copy_file(ldap_configured_file_path, ldap_original_file_path) + + if self.util.is_exist(pam_script_back_up_file_path): + self.util.copy_file(pam_script_configured_file_path, pam_script_original_file_path) + self.logger.info( + "{0} is replaced with {1}.".format(pam_script_original_file_path, pam_script_configured_file_path)) + else: + self.logger.info("Backing up {0}".format(pam_script_original_file_path)) + self.util.copy_file(pam_script_original_file_path, pam_script_back_up_file_path) + self.logger.info("{0} file is replaced with {1}".format(pam_script_original_file_path, + pam_script_configured_file_path)) + self.util.copy_file(pam_script_configured_file_path, pam_script_original_file_path) + + (result_code, p_out, p_err) = self.util.execute("DEBIAN_FRONTEND=noninteractive pam-auth-update --package") + if result_code == 0: + self.logger.info("'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' has run successfully") + else: + self.logger.error( + "'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' could not run successfully: " + p_err) + + # Configure nsswitch.conf + file_ns_switch = open("/etc/nsswitch.conf", 'r') + file_data = file_ns_switch.read() + + # cleared file data from spaces, tabs and newlines + text = pattern.sub('', file_data) + + is_configuration_done_before = False + if ("passwd:compatldap" not in text): + file_data = file_data.replace("passwd: compat", "passwd: compat ldap") + is_configuration_done_before = True + + if ("group:compatldap" not in text): + file_data = file_data.replace("group: compat", "group: compat ldap") + is_configuration_done_before = True + + if ("shadow:compatldap" not in text): + file_data = file_data.replace("shadow: compat", "shadow: compat ldap") + is_configuration_done_before = True + + if is_configuration_done_before: + self.logger.info("nsswitch.conf configuration has been completed") + else: + self.logger.info("nsswitch.conf is already configured") + + file_ns_switch.close() + file_ns_switch = open("/etc/nsswitch.conf", 'w') + file_ns_switch.write(file_data) + file_ns_switch.close() + + # configure ldap-cache + self.logger.info("Starting to ldap-cache configurations.") + result_code, p_out, p_err = self.util.execute("apt-get install nss-updatedb -y") + if result_code != 0: + self.logger.error("Error occured while downloading nss-updatedb.") + else: + self.logger.info("nss-updatedb downloaded successfully. Configuring /etc/nsswitch.conf.") + file_ns_switch = open("/etc/nsswitch.conf", 'r') + file_data = file_ns_switch.read() + + # cleared file data from spaces, tabs and newlines + text = pattern.sub('', file_data) + + did_configuration_change = False + if "passwd:compatldap[NOTFOUND=return]db" not in text: + file_data = file_data.replace("passwd: compat ldap", + "passwd: compat ldap [NOTFOUND=return] db") + did_configuration_change = True + + if "group:compatldap[NOTFOUND=return]db" not in text: + file_data = file_data.replace("group: compat ldap", + "group: compat ldap [NOTFOUND=return] db") + did_configuration_change = True + + if "gshadow:files" in text and "#gshadow:files" not in text: + file_data = file_data.replace("gshadow: files", "#gshadow: files") + did_configuration_change = True + + if did_configuration_change: + self.logger.info("nsswitch.conf configuration has been configured for ldap cache.") + else: + self.logger.info("nsswitch.conf has already been configured for ldap cache.") + + file_ns_switch.close() + file_ns_switch = open("/etc/nsswitch.conf", 'w') + file_ns_switch.write(file_data) + file_ns_switch.close() + self.util.execute("nss_updatedb ldap") + + # create cron job for ldap cache + content = "#!/bin/bash\n" \ + "nss-updatedb ldap" + nss_update_cron_job_file_path = "/etc/cron.daily/nss-updatedb" + if self.util.is_exist(nss_update_cron_job_file_path): + self.logger.info( + "{0} exists. File will be deleted and creating new one.".format(nss_update_cron_job_file_path)) + self.util.delete_file(nss_update_cron_job_file_path) + self.util.create_file(nss_update_cron_job_file_path) + self.util.write_file(nss_update_cron_job_file_path, content, 'w+') + self.util.execute("chmod +x " + nss_update_cron_job_file_path) + else: + self.logger.info( + "{0} doesnt exist. File will be created and content will be written.".format( + nss_update_cron_job_file_path)) + self.util.create_file(nss_update_cron_job_file_path) + self.util.write_file(nss_update_cron_job_file_path, content, 'w+') + self.util.execute("chmod +x " + nss_update_cron_job_file_path) + + # configure /etc/libnss-ldap.conf + libnss_ldap_file_path = "/etc/libnss-ldap.conf" + content = "bind_policy hard" \ + "\nnss_reconnect_tries 1" \ + "\nnss_reconnect_sleeptime 1" \ + "\nnss_reconnect_maxsleeptime 8" \ + "\nnss_reconnect_maxconntries 2" + if self.util.is_exist(libnss_ldap_file_path): + self.logger.info("{0} exists.".format(libnss_ldap_file_path)) + self.util.execute("sed -i '/bind_policy hard/c\\' " + libnss_ldap_file_path) + self.util.execute("sed -i '/nss_reconnect_tries 1/c\\' " + libnss_ldap_file_path) + self.util.execute("sed -i '/nss_reconnect_sleeptime 1/c\\' " + libnss_ldap_file_path) + self.util.execute("sed -i '/nss_reconnect_maxsleeptime 8/c\\' " + libnss_ldap_file_path) + self.util.execute("sed -i '/nss_reconnect_maxconntries 2/c\\' " + libnss_ldap_file_path) + self.util.write_file(libnss_ldap_file_path, content, 'a+') + self.logger.info("Configuration has been made to {0}.".format(libnss_ldap_file_path)) + + result_code, p_out, p_err = self.util.execute("apt-get install libnss-db libpam-ccreds -y") + if result_code != 0: + self.logger.error("Error occured while downloading libnss-db libpam-ccreds.") + else: + self.logger.error("libnss-db libpam-ccreds are downloaded.") + + # configure sudo-ldap + sudo_ldap_conf_file_path = "/etc/sudo-ldap.conf" + content = "sudoers_base ou=Roles," + dn \ + + "\nBASE " + dn \ + + "\nURI ldap://" + server_address + # clean if config is already written + self.util.execute("sed -i '/BASE /c\\' " + sudo_ldap_conf_file_path) + self.util.execute("sed -i '/sudoers_base /c\\' " + sudo_ldap_conf_file_path) + self.util.execute("sed -i '/URI /c\\' " + sudo_ldap_conf_file_path) + + if self.util.is_exist(sudo_ldap_conf_file_path): + self.logger.info("{0} exists.".format(sudo_ldap_conf_file_path)) + self.util.write_file(sudo_ldap_conf_file_path, content, 'a+') + self.logger.info("Content is written to {0} successfully.".format(sudo_ldap_conf_file_path)) + + # Configure lightdm.service + # check if 99-pardus-xfce.conf exists if not create + pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" + if not self.util.is_exist(pardus_xfce_path): + self.logger.info("99-pardus-xfce.conf does not exist.") + self.util.create_file(pardus_xfce_path) + + file_lightdm = open(pardus_xfce_path, 'a') + file_lightdm.write("[Seat:*]\n") + file_lightdm.write("greeter-hide-users=true") + file_lightdm.close() + self.logger.info("lightdm has been configured.") + else: + self.logger.info("99-pardus-xfce.conf exists. Delete file and create new one.") + self.util.delete_file(pardus_xfce_path) + self.util.create_file(pardus_xfce_path) + + file_lightdm = open(pardus_xfce_path, 'a') + file_lightdm.write("[Seat:*]") + file_lightdm.write("greeter-hide-users=true") + file_lightdm.close() + self.logger.info("lightdm.conf has been configured.") + self.util.execute("systemctl restart nscd.service") + self.util.execute("pam-auth-update --force") + self.logger.info("LDAP Login operation has been completed.") + + self.logger.info("LDAP Login işlemi başarı ile sağlandı.") + except Exception as e: + self.logger.error(str(e)) + self.logger.info("LDAP Login işlemi esnasında hata oluştu.") + raise Exception('LDAP Ayarları yapılırken hata oluştu. Lütfen ağ bağlantınızı kontrol ediniz. Deponuzun güncel olduğundan emin olunuz.') \ No newline at end of file diff --git a/usr/share/ahenk/base/registration/execute_sssd_authentication.py b/usr/share/ahenk/base/registration/execute_sssd_authentication.py new file mode 100644 index 0000000..6d9ceee --- /dev/null +++ b/usr/share/ahenk/base/registration/execute_sssd_authentication.py @@ -0,0 +1,150 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Hasan Kara + +from base.scope import Scope +from base.util.util import Util +import re + + +class ExecuteSSSDAuthentication: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.util = Util() + + def authenticate(self, server_address, dn, admin_dn, admin_password): + try: + ldap_pwdlockout_dn = "cn=DefaultPolicy,ou=PasswordPolicies" + "," + dn + + # pattern for clearing file data from spaces, tabs and newlines + pattern = re.compile(r'\s+') + + sssd_config_template_path = "/usr/share/ahenk/base/registration/config-files/sssd.conf" + sssd_config_folder_path = "/etc/sssd" + sssd_config_file_path = "/etc/sssd/sssd.conf" + + common_session_conf_path = "/etc/pam.d/common-session" + + # copy configuration file to /etc/sssd/sssd.conf before package installation + # create sssd folder in /etc + if not self.util.is_exist(sssd_config_folder_path): + self.util.create_directory(sssd_config_folder_path) + self.logger.info("{0} folder is created".format(sssd_config_folder_path)) + + # Copy sssd.conf template under /etc/sssd + self.util.copy_file(sssd_config_template_path, sssd_config_folder_path) + self.logger.info("{0} config file is copied under {1}".format(sssd_config_template_path, sssd_config_folder_path)) + + # Configure sssd.conf + file_sssd = open(sssd_config_file_path, 'r') + file_data = file_sssd.read() + + file_data = file_data.replace("###ldap_pwdlockout_dn###", "ldap_pwdlockout_dn = " + ldap_pwdlockout_dn) + file_data = file_data.replace("###ldap_uri###", "ldap_uri = " + "ldap://" + server_address + "/") + file_data = file_data.replace("###ldap_default_bind_dn###", "ldap_default_bind_dn = " + admin_dn) + file_data = file_data.replace("###ldap_default_authtok###", "ldap_default_authtok = " + admin_password) + file_data = file_data.replace("###ldap_search_base###", "ldap_search_base = " + dn) + file_data = file_data.replace("###ldap_user_search_base###", "ldap_user_search_base = " + dn) + file_data = file_data.replace("###ldap_group_search_base###", "ldap_group_search_base = " + dn) + + file_sssd.close() + file_sssd = open(sssd_config_file_path, 'w') + file_sssd.write(file_data) + file_sssd.close() + + # Install libpam-sss sssd-common for sssd authentication + (result_code, p_out, p_err) = self.util.execute("sudo apt install libpam-sss sssd-common -y") + + if result_code != 0: + self.logger.error("SSSD packages couldn't be downloaded.") + return False + + # configure common-session for creating home directories for ldap users + file_common_session = open(common_session_conf_path, 'r') + file_data = file_common_session.read() + + if "session optional pam_mkhomedir.so skel=/etc/skel umask=077" not in file_data : + file_data = file_data + "\n" + "session optional pam_mkhomedir.so skel=/etc/skel umask=077" + self.logger.info("common-session is configured") + + file_common_session.close() + file_common_session = open(common_session_conf_path, 'w') + file_common_session.write(file_data) + file_common_session.close() + + # Configure nsswitch.conf + file_ns_switch = open("/etc/nsswitch.conf", 'r') + file_data = file_ns_switch.read() + + # cleared file data from spaces, tabs and newlines + text = pattern.sub('', file_data) + + is_configuration_done_before = False + if "passwd:compatsss" not in text: + file_data = file_data.replace("passwd: compat", "passwd: compat sss") + is_configuration_done_before = True + + if "group:compatsss" not in text: + file_data = file_data.replace("group: compat", "group: compat sss") + is_configuration_done_before = True + + if "shadow:compatsss" not in text: + file_data = file_data.replace("shadow: compat", "shadow: compat sss") + is_configuration_done_before = True + + if "services:dbfilessss" not in text: + file_data = file_data.replace("services: db files", "services: db files sss") + is_configuration_done_before = True + + if "netgroup:nissss" not in text: + file_data = file_data.replace("netgroup: nis", "netgroup: nis sss") + is_configuration_done_before = True + + if "sudoers:filessss" not in text: + file_data = file_data.replace("sudoers: files", "sudoers: files sss") + is_configuration_done_before = True + + if is_configuration_done_before: + self.logger.info("nsswitch.conf configuration has been completed") + else: + self.logger.info("nsswitch.conf is already configured") + + file_ns_switch.close() + file_ns_switch = open("/etc/nsswitch.conf", 'w') + file_ns_switch.write(file_data) + file_ns_switch.close() + + # Configure lightdm.service + # check if 99-pardus-xfce.conf exists if not create + pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" + if not self.util.is_exist(pardus_xfce_path): + self.logger.info("99-pardus-xfce.conf does not exist.") + self.util.create_file(pardus_xfce_path) + + file_lightdm = open(pardus_xfce_path, 'a') + file_lightdm.write("[Seat:*]\n") + file_lightdm.write("greeter-hide-users=true") + file_lightdm.close() + self.logger.info("lightdm has been configured.") + else: + self.logger.info("99-pardus-xfce.conf exists. Delete file and create new one.") + self.util.delete_file(pardus_xfce_path) + self.util.create_file(pardus_xfce_path) + + file_lightdm = open(pardus_xfce_path, 'a') + file_lightdm.write("[Seat:*]") + file_lightdm.write("greeter-hide-users=true") + file_lightdm.close() + self.logger.info("lightdm.conf has been configured.") + + self.util.execute("systemctl restart nscd.service") + self.util.execute("pam-auth-update --force") + self.logger.info("LDAP Login operation has been completed.") + + self.logger.info("LDAP Login işlemi başarı ile sağlandı.") + return True + except Exception as e: + self.logger.error(str(e)) + self.logger.info("LDAP Login işlemi esnasında hata oluştu.") + return False diff --git a/usr/share/ahenk/base/registration/registration.py b/usr/share/ahenk/base/registration/registration.py new file mode 100644 index 0000000..7818084 --- /dev/null +++ b/usr/share/ahenk/base/registration/registration.py @@ -0,0 +1,412 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import datetime +import json +import uuid +from uuid import getnode as get_mac +from base.scope import Scope +from base.messaging.anonymous_messenger import AnonymousMessenger +from base.system.system import System +from base.util.util import Util +from helper import system as sysx +import pwd +from base.timer.setup_timer import SetupTimer +from base.timer.timer import Timer +import re +import os +from base.registration.execute_cancel_ldap_login import ExecuteCancelLDAPLogin +from base.registration.execute_ldap_login import ExecuteLDAPLogin + +class Registration: + def __init__(self): + scope = Scope().get_instance() + self.logger = scope.get_logger() + self.message_manager = scope.get_message_manager() + self.event_manager = scope.get_event_manager() + self.messenger = scope.get_messenger() + self.conf_manager = scope.get_configuration_manager() + self.db_service = scope.get_db_service() + self.util = Util() + self.servicename='im.liderahenk.org' + + #self.event_manager.register_event('REGISTRATION_RESPONSE', self.registration_process) + self.event_manager.register_event('REGISTRATION_SUCCESS', self.registration_success) + self.event_manager.register_event('REGISTRATION_ERROR', self.registration_error) + + self.ldap_login_cancel = ExecuteCancelLDAPLogin() + self.ldap_login = ExecuteLDAPLogin() + + if self.is_registered(): + self.logger.debug('Ahenk already registered') + else: + self.register(True) + + def registration_request(self, hostname,username,password): + + self.logger.debug('Requesting registration') + # SetupTimer.start(Timer(System.Ahenk.registration_timeout(), timeout_function=self.registration_timeout,checker_func=self.is_registered, kwargs=None)) + + self.servicename = self.conf_manager.get("CONNECTION", "servicename") + + self.host = hostname + self.user_name = username + self.user_password= password + + if(username is None and password is None and self.host is None): + + self.host = self.conf_manager.get("CONNECTION", "host") + + user_name= os.getlogin() + self.logger.debug('User : '+ str(user_name)) + pout = Util.show_registration_message(user_name,'Makineyi Lider MYS sistemine kaydetmek için bilgileri ilgili alanlara giriniz. LÜTFEN DEVAM EDEN İŞLEMLERİ SONLANDIRDIĞINZA EMİN OLUNUZ !', + 'LIDER MYS SISTEMINE KAYIT', self.host) + self.logger.debug('pout : ' + str(pout)) + field_values = pout.split(' ') + user_registration_info = list(field_values) + + if self.host == '': + self.host = user_registration_info[0] + self.user_name = user_registration_info[1] + self.user_password = user_registration_info[2] + else: + self.user_name = user_registration_info[0] + self.user_password = user_registration_info[1] + + #anon_messenger = AnonymousMessenger(self.message_manager.registration_msg(user_name,user_password), self.host,self.servicename) + #anon_messenger.connect_to_server() + + self.logger.debug('Requesting registration') + SetupTimer.start(Timer(System.Ahenk.registration_timeout(), timeout_function=self.registration_timeout,checker_func=self.is_registered, kwargs=None)) + anon_messenger = AnonymousMessenger(self.message_manager.registration_msg(self.user_name,self.user_password), self.host,self.servicename) + anon_messenger.connect_to_server() + + def ldap_registration_request(self): + self.logger.info('Requesting LDAP registration') + self.messenger.send_Direct_message(self.message_manager.ldap_registration_msg()) + + def registration_success(self, reg_reply): + self.logger.info('Registration update starting') + try: + dn = str(reg_reply['agentDn']) + self.logger.info('Current dn:' + dn) + self.logger.info('updating host name and service') + self.update_registration_attrs(dn) + self.install_and_config_ldap(reg_reply) + + except Exception as e: + self.logger.error('Registartion error. Error Message: {0}.'.format(str(e))) + print(e) + raise + + def update_registration_attrs(self, dn=None): + self.logger.debug('Registration configuration is updating...') + self.db_service.update('registration', ['dn', 'registered'], [dn, 1], ' registered = 0') + + if self.conf_manager.has_section('CONNECTION'): + self.conf_manager.set('CONNECTION', 'uid', + self.db_service.select_one_result('registration', 'jid', ' registered=1')) + self.conf_manager.set('CONNECTION', 'password', + self.db_service.select_one_result('registration', 'password', ' registered=1')) + + if self.host and self.servicename: + self.conf_manager.set('CONNECTION', 'host', self.host) + self.conf_manager.set('CONNECTION', 'servicename', self.servicename) + + # TODO get file path? + with open('/etc/ahenk/ahenk.conf', 'w') as configfile: + self.conf_manager.write(configfile) + self.logger.debug('Registration configuration file is updated') + + def install_and_config_ldap(self, reg_reply): + self.logger.info('ldap install process starting') + server_address = str(reg_reply['ldapServer']) + dn = str(reg_reply['ldapBaseDn']) + version = str(reg_reply['ldapVersion']) + admin_dn = str(reg_reply['ldapUserDn']) # get user full dn from server.. password same + #admin_password = self.user_password # same user get from server + admin_password = self.db_service.select_one_result('registration', 'password', ' registered=1') + if server_address != '' and dn != '' and version != '' and admin_dn != '' and admin_password != '': + self.logger.info("PAM LDAP configuration process starting....") + self.ldap_login.login(server_address,dn,version,admin_dn,admin_password) + self.logger.info("PAM LDAP configuration process starting....") + else : + raise Exception( + 'LDAP Ayarları yapılırken hata oluştu. Lütfen ağ bağlantınızı kontrol ediniz. Deponuzun güncel olduğundan emin olunuz.') + + def registration_error(self, reg_reply): + self.re_register() + + def is_registered(self): + try: + if str(System.Ahenk.uid()): + return True + else: + return False + except: + return False + + def is_ldap_registered(self): + dn = self.db_service.select_one_result('registration', 'dn', 'registered = 1') + if dn is not None and dn != '': + return True + else: + return False + + def register(self, uuid_depend_mac=False): + cols = ['jid', 'password', 'registered', 'params', 'timestamp'] + vals = [str(System.Os.hostname()), str(self.generate_uuid(uuid_depend_mac)), 0, + str(self.get_registration_params()), str(datetime.datetime.now().strftime("%d-%m-%Y %I:%M"))] + + self.db_service.delete('registration', ' 1==1 ') + self.db_service.update('registration', cols, vals) + self.logger.debug('Registration parameters were created') + + def get_registration_params(self): + parts = [] + for part in System.Hardware.Disk.partitions(): + parts.append(part[0]) + + params = { + 'ipAddresses': str(System.Hardware.Network.ip_addresses()).replace('[', '').replace(']', ''), + 'macAddresses': str(System.Hardware.Network.mac_addresses()).replace('[', '').replace(']', ''), + 'hostname': System.Os.hostname(), + 'os.name': System.Os.name(), + 'os.version': System.Os.version(), + 'os.kernel': System.Os.kernel_release(), + 'os.distributionName': System.Os.distribution_name(), + 'os.distributionId': System.Os.distribution_id(), + 'os.distributionVersion': System.Os.distribution_version(), + 'os.architecture': System.Os.architecture(), + 'hardware.cpu.architecture': System.Hardware.Cpu.architecture(), + 'hardware.cpu.logicalCoreCount': System.Hardware.Cpu.logical_core_count(), + 'hardware.cpu.physicalCoreCount': System.Hardware.Cpu.physical_core_count(), + 'hardware.disk.total': System.Hardware.Disk.total(), + 'hardware.disk.used': System.Hardware.Disk.used(), + 'hardware.disk.free': System.Hardware.Disk.free(), + 'hardware.disk.partitions': str(parts), + 'hardware.monitors': str(System.Hardware.monitors()), + 'hardware.screens': str(System.Hardware.screens()), + 'hardware.usbDevices': str(System.Hardware.usb_devices()), + 'hardware.printers': str(System.Hardware.printers()), + 'hardware.systemDefinitions': str(System.Hardware.system_definitions()), + 'hardware.model.version': str(System.Hardware.machine_model()), + 'hardware.memory.total': System.Hardware.Memory.total(), + 'hardware.network.ipAddresses': str(System.Hardware.Network.ip_addresses()), + 'sessions.userNames': str(System.Sessions.user_name()), + 'bios.releaseDate': System.BIOS.release_date()[1].replace('\n', '') if System.BIOS.release_date()[ + 0] == 0 else 'n/a', + 'bios.version': System.BIOS.version()[1].replace('\n', '') if System.BIOS.version()[0] == 0 else 'n/a', + 'bios.vendor': System.BIOS.vendor()[1].replace('\n', '') if System.BIOS.vendor()[0] == 0 else 'n/a', + 'hardware.baseboard.manufacturer': System.Hardware.BaseBoard.manufacturer()[1].replace('\n', '') if + System.Hardware.BaseBoard.manufacturer()[0] == 0 else 'n/a', + 'hardware.baseboard.version': System.Hardware.BaseBoard.version()[1].replace('\n', '') if + System.Hardware.BaseBoard.version()[0] == 0 else 'n/a', + 'hardware.baseboard.assetTag': System.Hardware.BaseBoard.asset_tag()[1].replace('\n', '') if + System.Hardware.BaseBoard.asset_tag()[0] == 0 else 'n/a', + 'hardware.baseboard.productName': System.Hardware.BaseBoard.product_name()[1].replace('\n', '') if + System.Hardware.BaseBoard.product_name()[0] == 0 else 'n/a', + 'hardware.baseboard.serialNumber': System.Hardware.BaseBoard.serial_number()[1].replace('\n', '') if + System.Hardware.BaseBoard.serial_number()[0] == 0 else 'n/a', + } + + return json.dumps(params) + + def unregister(self): + self.logger.debug('Ahenk is unregistering...') + self.db_service.delete('registration', ' 1==1 ') + self.logger.debug('Ahenk is unregistered') + + def re_register(self): + self.logger.debug('Reregistrating...') + self.unregister() + self.register(False) + + def generate_uuid(self, depend_mac=True): + if depend_mac is False: + self.logger.debug('uuid creating randomly') + return uuid.uuid4() # make a random UUID + else: + self.logger.debug('uuid creating according to mac address') + return uuid.uuid3(uuid.NAMESPACE_DNS, + str(get_mac())) # make a UUID using an MD5 hash of a namespace UUID and a mac address + + def generate_password(self): + return uuid.uuid4() + + def registration_timeout(self): + self.logger.error( + 'Could not reach registration response from Lider. Be sure XMPP server is reachable and it supports anonymous message, Lider is running properly ' + 'and it is connected to XMPP server! Check your Ahenk configuration file (/etc/ahenk/ahenk.conf)') + self.logger.error('Ahenk is shutting down...') + print('Ahenk is shutting down...') + Util.show_message(os.getlogin(),':0',"Lider MYS sistemine ulaşılamadı. Lütfen sunucu adresini kontrol ediniz....","HATA") + System.Process.kill_by_pid(int(System.Ahenk.get_pid_number())) + + def purge_and_unregister(self): + try: + self.logger.info('Ahenk conf cleaned') + self.logger.info('Ahenk conf cleaning from db') + self.unregister() + self.ldap_login_cancel.cancel(); + self.logger.info('Cleaning ahenk conf..') + self.clean() + self.logger.info('Ahenk conf cleaned from db') + self.logger.info('Enable Users') + self.enable_local_users() + Util.shutdown() + except Exception as e: + self.logger.error("Error while running purge_and_unregister process.. Error Message " + str(e)) + #System.Process.kill_by_pid(int(System.Ahenk.get_pid_number())) + #sys.exit(2) + + def change_configs_after_purge(self): + # pattern for clearing file data from spaces, tabs and newlines + pattern = re.compile(r'\s+') + + ldap_back_up_file_path = "/usr/share/ahenk/pam_scripts_original/ldap" + ldap_original_file_path = "/usr/share/pam-configs/ldap" + + pam_script_back_up_file_path = "/usr/share/ahenk/pam_scripts_original/pam_script" + pam_script_original_file_path = "/usr/share/pam-configs/pam_script" + + if self.util.is_exist(ldap_back_up_file_path): + self.logger.info("Replacing {0} with {1}".format(ldap_original_file_path, ldap_back_up_file_path)) + self.util.copy_file(ldap_back_up_file_path, ldap_original_file_path) + self.logger.info("Deleting {0}".format(ldap_back_up_file_path)) + self.util.delete_file(ldap_back_up_file_path) + + if self.util.is_exist(pam_script_back_up_file_path): + self.logger.info( + "Replacing {0} with {1}".format(pam_script_original_file_path, pam_script_back_up_file_path)) + self.util.copy_file(pam_script_back_up_file_path, pam_script_original_file_path) + self.logger.info("Deleting {0}".format(pam_script_back_up_file_path)) + self.util.delete_file(pam_script_back_up_file_path) + + (result_code, p_out, p_err) = self.util.execute("DEBIAN_FRONTEND=noninteractive pam-auth-update --package") + if result_code == 0: + self.logger.info("'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' has run successfully") + else: + self.logger.error( + "'DEBIAN_FRONTEND=noninteractive pam-auth-update --package' could not run successfully: " + p_err) + + # Configure nsswitch.conf + file_ns_switch = open("/etc/nsswitch.conf", 'r') + file_data = file_ns_switch.read() + + # cleared file data from spaces, tabs and newlines + text = pattern.sub('', file_data) + + did_configuration_change = False + if "passwd:compatldap[NOTFOUND=return]db" in text: + file_data = file_data.replace("passwd: compat ldap [NOTFOUND=return] db", "passwd: compat") + did_configuration_change = True + + if "group:compatldap[NOTFOUND=return]db" in text: + file_data = file_data.replace("group: compat ldap [NOTFOUND=return] db", "group: compat") + did_configuration_change = True + + if "shadow:compatldap" in text: + file_data = file_data.replace("shadow: compat ldap", "shadow: compat") + did_configuration_change = True + + if "#gshadow:files" in text: + file_data = file_data.replace("#gshadow: files", "gshadow: files") + did_configuration_change = True + + if did_configuration_change: + self.logger.info("nsswitch.conf configuration has been configured") + else: + self.logger.info("nsswitch.conf has already been configured") + + file_ns_switch.close() + file_ns_switch = open("/etc/nsswitch.conf", 'w') + file_ns_switch.write(file_data) + file_ns_switch.close() + + # Configure ldap-cache + nss_update_cron_job_file_path = "/etc/cron.daily/nss-updatedb" + if self.util.is_exist(nss_update_cron_job_file_path): + self.util.delete_file(nss_update_cron_job_file_path) + self.logger.info("{0} is deleted.".format(nss_update_cron_job_file_path)) + + # Configure lightdm.service + pardus_xfce_path = "/usr/share/lightdm/lightdm.conf.d/99-pardus-xfce.conf" + if self.util.is_exist(pardus_xfce_path): + self.logger.info("99-pardus-xfce.conf exists. Deleting file.") + self.util.delete_file(pardus_xfce_path) + + self.util.execute("systemctl restart nscd.service") + self.logger.info("Operation finished") + + def clean(self): + print('Ahenk cleaning..') + import configparser + try: + config = configparser.ConfigParser() + config._interpolation = configparser.ExtendedInterpolation() + config.read(System.Ahenk.config_path()) + db_path = config.get('BASE', 'dbPath') + + if Util.is_exist(System.Ahenk.fifo_file()): + Util.delete_file(System.Ahenk.fifo_file()) + + if Util.is_exist(db_path): + Util.delete_file(db_path) + + if Util.is_exist(System.Ahenk.pid_path()): + Util.delete_file(System.Ahenk.pid_path()) + + config.set('CONNECTION', 'uid', '') + config.set('CONNECTION', 'password', '') + config.set('MACHINE', 'user_disabled', '0') + + with open(System.Ahenk.config_path(), 'w') as file: + config.write(file) + file.close() + print('Ahenk cleaned.') + except Exception as e: + self.logger.error("Error while running clean command. Error Message " + str(e)) + print('Error while running clean command. Error Message {0}'.format(str(e))) + + def enable_local_users(self): + passwd_cmd = 'passwd -u {}' + change_home = 'usermod -m -d {0} {1}' + change_username = 'usermod -l {0} {1}' + content = self.util.read_file('/etc/passwd') + for p in pwd.getpwall(): + if not sysx.shell_is_interactive(p.pw_shell): + continue + if p.pw_uid == 0: + continue + if p.pw_name in content: + new_home_dir = p.pw_dir.rstrip('-local/') + '/' + new_username = p.pw_name.rstrip('-local') + self.util.execute(passwd_cmd.format(p.pw_name)) + self.util.execute(change_username.format(new_username, p.pw_name)) + self.util.execute(change_home.format(new_home_dir, new_username)) + self.logger.debug("User: '{0}' will be enabled and changed username and home directory of username".format(p.pw_name)) + + + def disable_local_users(self): + passwd_cmd = 'passwd -l {}' + change_home = 'usermod -m -d {0} {1}' + change_username = 'usermod -l {0} {1}' + content = Util.read_file('/etc/passwd') + kill_all_process = 'killall -KILL -u {}' + change_permisson = "chmod -R 700 {}" + for p in pwd.getpwall(): + self.logger.info("User: '{0}' will be disabled and changed username and home directory of username".format(p.pw_name)) + if not sysx.shell_is_interactive(p.pw_shell): + continue + if p.pw_uid == 0: + continue + if p.pw_name in content: + new_home_dir = p.pw_dir.rstrip('/') + '-local/' + new_username = p.pw_name+'-local' + Util.execute(kill_all_process.format(p.pw_name)) + Util.execute(passwd_cmd.format(p.pw_name)) + Util.execute(change_username.format(new_username, p.pw_name)) + Util.execute(change_home.format(new_home_dir, new_username)) + Util.execute(change_permisson.format(new_home_dir)) \ No newline at end of file diff --git a/usr/share/ahenk/base/registration/scripts/ldap-login.sh b/usr/share/ahenk/base/registration/scripts/ldap-login.sh new file mode 100755 index 0000000..e9bbcd4 --- /dev/null +++ b/usr/share/ahenk/base/registration/scripts/ldap-login.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +#Author: +#set debconf libnss-ldap and libpam-ldap + +ldap_hostname=$1 +ldap_base_dn=$2 +ldap_user_dn=$3 +ldap_user_pwd=$4 +ldap_version=$5 + +echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections + +## libnss-ldap +echo -e " \ +libnss-ldap libnss-ldap/dblogin boolean false +libnss-ldap shared/ldapns/base-dn string $ldap_base_dn +libnss-ldap libnss-ldap/binddn string $ldap_user_dn +libnss-ldap libnss-ldap/dbrootlogin boolean true +libnss-ldap libnss-ldap/override boolean true +libnss-ldap shared/ldapns/ldap-server string $ldap_hostname +libnss-ldap libnss-ldap/confperm boolean false +libnss-ldap libnss-ldap/rootbinddn string $ldap_user_dn +libnss-ldap shared/ldapns/ldap_version select $ldap_version +libnss-ldap libnss-ldap/nsswitch note +libpam-ldap libpam-ldap/dblogin boolean false +libpam-ldap libpam-ldap/dbrootlogin boolean true +libpam-ldap libpam-ldap/override boolean true +libpam-ldap libpam-ldap/pam_password string crypt +libpam-ldap libpam-ldap/rootbinddn string $ldap_user_dn +libpam-ldap libpam-runtime/override boolean false \ +" | debconf-set-selections + +echo "Name: libnss-ldap/bindpw +Template: libnss-ldap/bindpw +Owners: libnss-ldap, libnss-ldap:amd64 + +Name: libnss-ldap/rootbindpw +Template: libnss-ldap/rootbindpw +Value: +Owners: libnss-ldap, libnss-ldap:amd64 +Flags: seen + +Name: libpam-ldap/bindpw +Template: libpam-ldap/bindpw +Owners: libpam-ldap, libpam-ldap:amd64 + +Name: libpam-ldap/rootbindpw +Template: libpam-ldap/rootbindpw +Value: +Owners: libpam-ldap, libpam-ldap:amd64 +Flags: seen +Variables: + filename = /etc/pam_ldap.secret + package = libpam-ldap" >> /var/cache/debconf/passwords.dat + +echo $ldap_user_pwd > /etc/pam_ldap.secret +apt update +apt-get install libpam-ldap libnss-ldap ldap-utils -y +SUDO_FORCE_REMOVE=yes apt-get install sudo-ldap -y \ No newline at end of file diff --git a/usr/share/ahenk/base/scheduler/__init__.py b/usr/share/ahenk/base/scheduler/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/scheduler/base_scheduler.py b/usr/share/ahenk/base/scheduler/base_scheduler.py new file mode 100644 index 0000000..76241fe --- /dev/null +++ b/usr/share/ahenk/base/scheduler/base_scheduler.py @@ -0,0 +1,25 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN + +class BaseScheduler(object): + + def initialize(self): + # Implement this from your implementation class + pass + + def add_job(self): + # Implement this from your implementation class + pass + + def add_job_by_hour(self): + # Implement this from your implementation class + pass + + def add_job_by_mount(self): + # Implement this from your implementation class + pass + + def add_job_by_minute(self): + # Implement this from your implementation class + pass diff --git a/usr/share/ahenk/base/scheduler/custom/__init__.py b/usr/share/ahenk/base/scheduler/custom/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/scheduler/custom/all_match.py b/usr/share/ahenk/base/scheduler/custom/all_match.py new file mode 100644 index 0000000..f4c549d --- /dev/null +++ b/usr/share/ahenk/base/scheduler/custom/all_match.py @@ -0,0 +1,7 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +# Some utility classes / functions first +class AllMatch(set): + """Universal set - match everything""" + def __contains__(self, item): return True \ No newline at end of file diff --git a/usr/share/ahenk/base/scheduler/custom/custom_scheduler.py b/usr/share/ahenk/base/scheduler/custom/custom_scheduler.py new file mode 100644 index 0000000..1dac2d6 --- /dev/null +++ b/usr/share/ahenk/base/scheduler/custom/custom_scheduler.py @@ -0,0 +1,75 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN + +import time +from datetime import datetime, timedelta + +from base.scheduler.base_scheduler import BaseScheduler +from base.scheduler.custom.schedule_job import ScheduleTaskJob +from base.scheduler.custom.scheduledb import ScheduleTaskDB +from base.scope import Scope + + +class CustomScheduler(BaseScheduler): + def __init__(self): + self.events = [] + self.keep_run = True + self.logger = Scope.get_instance().get_logger() + self.scheduledb = ScheduleTaskDB() + + def initialize(self): + self.scheduledb.initialize() + tasks = self.scheduledb.load() + if tasks: + for task in tasks: + self.add_job(ScheduleTaskJob(task)) + + def add_job(self, job): + self.events.append(job) + + def save_and_add_job(self, task): + try: + self.logger.debug('Saving scheduled task to database...') + self.scheduledb.save(task) + self.logger.debug('Adding scheduled task to events...') + self.events.append(ScheduleTaskJob(task)) + except Exception as e: + self.logger.error( + 'A problem occurred while saving and adding job. Error Message: {0}'.format(str(e))) + + # unused + def remove_job(self, task_id): + for event in self.events: + if event.task.get_id() == task_id: + self.scheduledb.delete(task_id) + self.logger.debug('Task was deleted from scheduled tasks table') + self.events.remove(event) + self.logger.debug('Task was removed from events') + + # unused + def remove_job_via_task_id(self, task_id): + for event in self.events: + if event.task.get_id() == task_id: + self.scheduledb.delete(event.task) + self.events.remove(event) + + def stop(self): + self.keep_run = False + + def list_schedule_tasks(self): + return self.scheduledb.load() + + def run(self): + t = datetime(*datetime.now().timetuple()[:5]) + + while 1 and self.keep_run: + if self.events is not None and len(self.events) > 0: + + for e in self.events: + e.check(t) + + t += timedelta(minutes=1) + if datetime.now() < t: + sl = (t - datetime.now()).seconds + time.sleep(sl) diff --git a/usr/share/ahenk/base/scheduler/custom/schedule_job.py b/usr/share/ahenk/base/scheduler/custom/schedule_job.py new file mode 100644 index 0000000..c6f5b1c --- /dev/null +++ b/usr/share/ahenk/base/scheduler/custom/schedule_job.py @@ -0,0 +1,106 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN + +from base.scheduler.custom.all_match import AllMatch +from base.scope import Scope + + +class ScheduleTaskJob(object): + def __init__(self, task): + scope = Scope.get_instance() + + self.logger = scope.get_logger() + self.task_manager = scope.get_task_manager() + self.plugin_manager = scope.get_plugin_manager() + self.task = task + cron_sj = self.parse_cron_str(task.get_cron_str()) + try: + if cron_sj: + self.mins = self.conv_to_set(cron_sj[0]) + self.hours = self.conv_to_set(cron_sj[1]) + self.days = self.conv_to_set(cron_sj[2]) + self.months = self.conv_to_set(cron_sj[3]) + self.dow = self.conv_to_set(cron_sj[4]) + self.action = self.process_task + self.logger.debug('Instance created.') + except Exception as e: + self.logger.error( + 'A problem occurred while creating instance of ScheduleTaskJob. Error Message : {0}'.format( + str(e))) + + def process_task(self): + try: + self.logger.debug('Running scheduled task now...') + self.plugin_manager.process_task(self.task) + self.logger.debug('Scheduled Task was executed.') + # There is no any single shot task + # if self.is_single_shot(): + # Scope.getInstance().get_scheduler().remove_job(self.task.get_id()) + except Exception as e: + self.logger.error( + 'A problem occurred while running scheduled task. Error Message: {0}'.format(str(e))) + + def parse_cron_str(self, cron_str): + self.logger.debug('Parsing cron string...') + try: + cron_exp_arr = cron_str.split(" ") + cron_sj = [] + count = 0 + for exp in cron_exp_arr: + if exp.isdigit(): + cron_sj.append(exp) + elif '*' == exp: + cron_sj.append(AllMatch()) + elif '/' in exp: + range_val = int(exp.split("/")[1]) + if count == 0: + cron_sj.append(range(0, 60, range_val)) + elif count == 1: + cron_sj.append(range(0, 24, range_val)) + elif count == 2: + cron_sj.append(range(0, 7, range_val)) + elif count == 3: + cron_sj.append(range(0, 12, range_val)) + elif count == 3: + cron_sj.append(range(0, 7, range_val)) + else: + self.logger.warning('Unsupported expression.') + elif ',' in exp: + cron_sj.append("(" + str(exp) + ")") + else: + self.logger.warning('Unsupported expression.') + count += 1 + return cron_sj + except Exception as e: + self.logger.error( + 'A problem occurred while parsing cron expression. Error Message: {0}'.format(str(e))) + + def conv_to_set(self, obj): + self.logger.debug('Converting {0} to set'.format(str(obj))) + + if str(obj).isdigit(): + return set([int(obj)]) + if not isinstance(obj, set): + obj = set(obj) + + return obj + + # def is_single_shot(self): + # if '*' in self.task.cron_str: + # return True + # else: + # return False + + def matchtime(self, t): + """Return True if this event should trigger at the specified datetime""" + + return ((t.minute in self.mins) and + (t.hour in self.hours) and + (t.day in self.days) and + (t.month in self.months) and + (t.weekday() in self.dow)) + + def check(self, t): + if self.matchtime(t) is True: + self.action() diff --git a/usr/share/ahenk/base/scheduler/custom/scheduledb.py b/usr/share/ahenk/base/scheduler/custom/scheduledb.py new file mode 100644 index 0000000..6402a6e --- /dev/null +++ b/usr/share/ahenk/base/scheduler/custom/scheduledb.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN + +from base.model.task_bean import TaskBean +from base.model.plugin_bean import PluginBean +from base.scope import Scope + + +class ScheduleTaskDB(object): + def __init__(self): + scope = Scope.get_instance() + self.logger = scope.get_logger() + self.db_service = scope.get_db_service() + + def initialize(self): + self.logger.debug('Initializing scheduler database...') + self.db_service.check_and_create_table('schedule_task', + ['id INTEGER PRIMARY KEY AUTOINCREMENT', 'task_id TEXT']) + self.logger.debug('Scheduler database is ok.') + + def save(self, task): + self.logger.debug('Preparing schedule task for save operation... creating columns and values...') + cols = ['task_id'] + values = [task.get_id()] + self.logger.debug('Saving scheduler task to db... ') + self.db_service.update('schedule_task', cols, values, None) + self.logger.debug('Scheduler task saved.') + + def delete(self, task_id): + try: + self.logger.debug('Deleting schedule task. Task id=' + str(task_id)) + self.db_service.delete('schedule_task', 'task_id=' + str(task_id)) + self.logger.debug('Deleting schedule task deleted successfully. task id=' + str(task_id)) + except Exception as e: + self.logger.error('Exception occur when deleting schedule task ' + str(e)) + + def load(self): + try: + self.logger.debug('Loading schedule tasks...') + rows = self.db_service.select('schedule_task') + tasks = list() + for row in rows: + tasks.append(self.get_task_by_id(row[1])) + self.logger.debug( + 'Scheduled tasks are loaded successfully. Scheduled Tasks size is {0}'.format(str(len(tasks)))) + return tasks + except Exception as e: + self.logger.error( + 'Exception occurred while loading schedule tasks. Error Message: {0}'.format(str(e))) + + def get_task_by_id(self, task_id): + self.logger.debug('Getting task from db.') + try: + db_task = self.db_service.select('task', criteria='id={0}'.format(task_id))[0] + return TaskBean(db_task[0], db_task[1], db_task[2], db_task[3], db_task[4], db_task[5], + self.get_plugin_by_id(db_task[6]), db_task[7], db_task[8]) + except Exception as e: + self.logger.debug('A problem occurred while getting task by id. Error Message: {0}'.format(str(e))) + + def get_plugin_by_id(self, plugin_id): + self.logger.debug('Getting plugin from db.') + db_plugin = self.db_service.select('plugin', criteria='id={0}'.format(plugin_id))[0] + return PluginBean(db_plugin[0], db_plugin[1], db_plugin[2], db_plugin[3], db_plugin[4], db_plugin[5], + db_plugin[6], db_plugin[7], db_plugin[8], db_plugin[11], db_plugin[9], db_plugin[10], + db_plugin[12]) + diff --git a/usr/share/ahenk/base/scheduler/scheduler_factory.py b/usr/share/ahenk/base/scheduler/scheduler_factory.py new file mode 100644 index 0000000..3b55c9b --- /dev/null +++ b/usr/share/ahenk/base/scheduler/scheduler_factory.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN + +from base.scheduler.custom.custom_scheduler import CustomScheduler + +class SchedulerFactory(): + + def get_intstance(): + return CustomScheduler() + + get_intstance = staticmethod(get_intstance) \ No newline at end of file diff --git a/usr/share/ahenk/base/scope.py b/usr/share/ahenk/base/scope.py new file mode 100644 index 0000000..8fa8de7 --- /dev/null +++ b/usr/share/ahenk/base/scope.py @@ -0,0 +1,115 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# @author: İsmail BAŞARAN + + +class Scope(object): + """docstring for Scope""" + + scope_instance = None + + def __init__(self): + super(Scope, self).__init__() + self.custom_map = dict() + self.configuration_manager = None + self.message_manager = None + self.logger = None + self.plugin_manager = None + self.task_manager = None + self.response_queue = None + self.registration = None + self.event_manager = None + self.execution_manager = None + self.db_service = None + self.messenger = None + self.scheduler = None + + @staticmethod + def get_instance(): + return scope_instance + + @staticmethod + def set_instance(scope_obj): + global scope_instance + scope_instance = scope_obj + + def get_custom_map(self): + return self.custom_map + + def put_custom_map(self, name, value): + self.custom_map[str(name)] = value + + def get_custom_param(self, name): + return self.custom_map[str(name)] + + def get_configuration_manager(self): + return self.configuration_manager + + def set_configuration_manager(self, configuration_manager): + self.configuration_manager = configuration_manager + + def get_logger(self): + return self.logger + + def set_logger(self, logger): + self.logger = logger + + def get_message_manager(self): + return self.message_manager + + def set_message_manager(self, message_manager): + self.message_manager = message_manager + + def get_plugin_manager(self): + return self.plugin_manager + + def set_plugin_manager(self, plugin_manager): + self.plugin_manager = plugin_manager + + def get_task_manager(self): + return self.task_manager + + def set_task_manager(self, task_manager): + self.task_manager = task_manager + + def get_response_queue(self): + return self.response_queue + + def set_response_queue(self, response_queue): + self.response_queue = response_queue + + def get_registration(self): + return self.registration + + def set_registration(self, registration): + self.registration = registration + + def get_event_manager(self): + return self.event_manager + + def set_event_manager(self, event_manager): + self.event_manager = event_manager + + def get_execution_manager(self): + return self.execution_manager + + def set_execution_manager(self, execution_manager): + self.execution_manager = execution_manager + + def get_db_service(self): + return self.db_service + + def set_sb_service(self, db_service): + self.db_service = db_service + + def get_messenger(self): + return self.messenger + + def set_messenger(self, messenger): + self.messenger = messenger + + def set_scheduler(self, scheduler): + self.scheduler = scheduler + + def get_scheduler(self): + return self.scheduler diff --git a/usr/share/ahenk/base/system/system.py b/usr/share/ahenk/base/system/system.py new file mode 100644 index 0000000..5696515 --- /dev/null +++ b/usr/share/ahenk/base/system/system.py @@ -0,0 +1,654 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import configparser +import fcntl +import glob +import os +import platform +import re +import socket +import struct +import netifaces +from uuid import getnode as get_mac + +import cpuinfo +import psutil + +from base.scope import Scope +from base.util.util import Util + + +class System: + def __init__(self): + scope = Scope().get_instance() + self.db_service = scope.get_db_service() + self.logger = scope.get_logger() + + class BIOS(object): + @staticmethod + def vendor(): + try: + result_code, p_out, p_err = Util.execute('dmidecode --string bios-vendor') + return int(result_code), str(p_out), str(p_err) + except: + raise + + @staticmethod + def release_date(): + try: + result_code, p_out, p_err = Util.execute('dmidecode --string bios-release-date') + return int(result_code), str(p_out), str(p_err) + except: + raise + + @staticmethod + def version(): + try: + result_code, p_out, p_err = Util.execute('dmidecode --string bios-version') + return int(result_code), str(p_out), str(p_err) + except: + raise + + class Ahenk(object): + + @staticmethod + def installed_plugins(): + plugin_names = [] + possible_plugins = os.listdir(System.Ahenk.plugins_path()) + for plugin_name in possible_plugins: + location = os.path.join(System.Ahenk.plugins_path(), plugin_name) + if os.path.isdir(location) and System.Ahenk.module_name() + ".py" in os.listdir(location): + plugin_names.append(plugin_name) + return plugin_names + + @staticmethod + def db_path(): + config = configparser.ConfigParser() + config._interpolation = configparser.ExtendedInterpolation() + config.read(System.Ahenk.config_path()) + return config.get('BASE', 'dbPath') + + @staticmethod + def agreement_timeout(): + config = configparser.ConfigParser() + config._interpolation = configparser.ExtendedInterpolation() + config.read(System.Ahenk.config_path()) + return config.get('SESSION', 'agreement_timeout') + + @staticmethod + def registration_timeout(): + config = configparser.ConfigParser() + config._interpolation = configparser.ExtendedInterpolation() + config.read(System.Ahenk.config_path()) + return config.get('SESSION', 'registration_timeout') + + @staticmethod + def get_policy_timeout(): + config = configparser.ConfigParser() + config._interpolation = configparser.ExtendedInterpolation() + config.read(System.Ahenk.config_path()) + return config.get('SESSION', 'get_policy_timeout') + + @staticmethod + def uid(): + config = configparser.ConfigParser() + config._interpolation = configparser.ExtendedInterpolation() + config.read(System.Ahenk.config_path()) + return config.get('CONNECTION', 'uid') + + @staticmethod + def plugins_path(): + config = configparser.ConfigParser() + config._interpolation = configparser.ExtendedInterpolation() + config.read(System.Ahenk.config_path()) + return config.get('PLUGIN', 'pluginfolderpath') + + @staticmethod + def module_name(): + config = configparser.ConfigParser() + config._interpolation = configparser.ExtendedInterpolation() + config.read(System.Ahenk.config_path()) + return config.get('PLUGIN', 'mainModuleName') + + @staticmethod + def agreement(): + config = configparser.ConfigParser() + config._interpolation = configparser.ExtendedInterpolation() + config.read(System.Ahenk.config_path()) + return config.get('MACHINE', 'agreement') + + @staticmethod + def dn(): + system = System() + try: + dn = system.db_service.select_one_result('registration', 'dn', " registered='1'") + return dn + except: + return None + + @staticmethod + def ip(): + system = System() + try: + ip = system.db_service.select_one_result('session', 'ip') + return ip + except: + return None + + @staticmethod + def get_pid_number(): + pid_number = None + try: + if os.path.exists(System.Ahenk.pid_path()): + file = open(System.Ahenk.pid_path(), 'r') + pid_number = file.read() + file.close() + return pid_number + except Exception as e: + return None + + @staticmethod + def is_running(): + try: + if System.Ahenk.get_pid_number() is not None: + return psutil.pid_exists(int(System.Ahenk.get_pid_number())) + else: + return False + except Exception as e: + return False + + @staticmethod + def config_path(): + return '/etc/ahenk/ahenk.conf' + + @staticmethod + def pid_path(): + return '/var/run/ahenk.pid' + + @staticmethod + def fifo_file(): + return '/tmp/liderahenk.fifo' + + @staticmethod + def received_dir_path(): + path = '/tmp/' # move this to properties + if Util.is_exist(path) is False: + Util.create_directory(path) + Util.set_permission(path, '777') + return path + + class Process(object): + + @staticmethod + def process_by_pid(pid): + return psutil.Process(pid) + + @staticmethod + def pids(): + return psutil.pids() + + @staticmethod + def find_pids_by_name(p_name): + arr = [] + for pid in psutil.pids(): + if psutil.Process(id).name() == p_name: + arr.append(pid) + return arr + + @staticmethod + def is_running(pid): + return psutil.pid_exists(pid) + + @staticmethod + def kill_by_pid(pid): + return psutil.Process(pid).kill() + + @staticmethod + def kill_by_pids(pids): + for pid in pids: + psutil.Process(pid).kill() + + @staticmethod + def find_name_by_pid(pid): + return psutil.Process(pid).name() + + @staticmethod + def path(pid): + return psutil.Process(pid).exe() + + @staticmethod + def working_directory(pid): + return psutil.Process(pid).cwd() + + @staticmethod + def command_line(pid): + return psutil.Process(pid).cmdline() + + @staticmethod + def status(pid): + return psutil.Process(pid).status() + + @staticmethod + def username(pid): + return psutil.Process(pid).username() + + @staticmethod + def create_time(pid): + return psutil.Process(pid).create_time() + + @staticmethod + def cpu_times(pid): + return psutil.Process(pid).cpu_times() + + @staticmethod + def cpu_percent(pid): + return psutil.Process(pid).cpu_percent(interval=1.0) + + @staticmethod + def memory_percent(pid): + return psutil.Process(pid).memory_percent() + + @staticmethod + def open_files(pid): + return psutil.Process(pid).open_files() + + @staticmethod + def connections(pid): + return psutil.Process(pid).connections() + + @staticmethod + def threads(pid): + return psutil.Process(pid).threads() + + @staticmethod + def nice(pid): + return psutil.Process(pid).nice() + + @staticmethod + def environment(pid): + return psutil.Process(pid).environ() + + @staticmethod + def details(): + return psutil.test() + + class Sessions(object): + + @staticmethod + def user_name(): + arr = [] + for user in psutil.users(): + if str(user[0]) is not 'None' and user[0] not in arr: + arr.append(user[0]) + return arr + + @staticmethod + def user_details(): + return psutil.users() + + @staticmethod + def display(username): + system = System() + display = system.db_service.select_one_result('session', 'display', " username='{0}'".format(username)) + return display + + @staticmethod + def desktop(username): + system = System() + desktop = system.db_service.select_one_result('session', 'desktop', " username='{0}'".format(username)) + return desktop + + staticmethod + def userip(username): + system = System() + userip = system.db_service.select_one_result('session', 'ip', " username='{0}'".format(username)) + return userip + + @staticmethod + def user_home_path(username): + # TODO temp + return '/home/{0}/'.format(str(username)) + + class Os(object): + + @staticmethod + def architecture(): + return platform.architecture()[0] + + @staticmethod + def boot_time(): + return psutil.boot_time() + + @staticmethod + def file_format(): + return platform.architecture()[1] + + @staticmethod + def name(): + return platform.system() + + @staticmethod + def distribution_name(): + return platform.linux_distribution()[0] + + @staticmethod + def distribution_version(): + return platform.linux_distribution()[1] + + @staticmethod + def distribution_id(): + return platform.linux_distribution()[2] + + @staticmethod + def version(): + return platform.version() + + @staticmethod + def kernel_release(): + return platform.release() + + @staticmethod + def hostname(): + return platform.node() + + class Hardware(object): + + class BaseBoard(object): + + @staticmethod + def manufacturer(): + try: + result_code, p_out, p_err = Util.execute('dmidecode --string baseboard-manufacturer') + return int(result_code), str(p_out), str(p_err) + except: + raise + + @staticmethod + def product_name(): + try: + result_code, p_out, p_err = Util.execute('dmidecode --string baseboard-product-name') + return int(result_code), str(p_out), str(p_err) + except: + raise + + @staticmethod + def version(): + try: + result_code, p_out, p_err = Util.execute('dmidecode --string baseboard-version') + return int(result_code), str(p_out), str(p_err) + except: + raise + + @staticmethod + def serial_number(): + try: + result_code, p_out, p_err = Util.execute('dmidecode --string baseboard-serial-number') + return int(result_code), str(p_out), str(p_err) + except: + raise + + @staticmethod + def asset_tag(): + try: + result_code, p_out, p_err = Util.execute('dmidecode --string baseboard-asset-tag') + return int(result_code), str(p_out), str(p_err) + except: + raise + + class Memory(object): + + @staticmethod + def total(): + return int(int(psutil.virtual_memory()[0]) / (1024 * 1024)) + + @staticmethod + def available(): + return int(int(psutil.virtual_memory()[1]) / (1024 * 1024)) + + @staticmethod + def percent(): + return psutil.virtual_memory()[2] + + @staticmethod + def used(): + return int(int(psutil.virtual_memory()[3]) / (1024 * 1024)) + + @staticmethod + def free(): + return int(int(psutil.virtual_memory()[4]) / (1024 * 1024)) + + class Disk(object): + + @staticmethod + def total(): + return int(int(psutil.disk_usage('/')[0]) / (1024 * 1024)) + + @staticmethod + def used(): + return int(int(psutil.disk_usage('/')[1]) / (1024 * 1024)) + + @staticmethod + def free(): + return int(int(psutil.disk_usage('/')[2]) / (1024 * 1024)) + + @staticmethod + def percent(): + return psutil.disk_usage('/')[3] + + @staticmethod + def partitions(): + return psutil.disk_partitions() + + class Network(object): + + @staticmethod + def interface_size(): + return len(psutil.net_io_counters(pernic=True)) + + @staticmethod + def io_counter_detail(): + return psutil.net_io_counters(pernic=True) + + @staticmethod + def interfaces(): + arr = [] + for iface in psutil.net_io_counters(pernic=True): + arr.append(str(iface)) + return arr + + @staticmethod + def ip_addresses(): + arr = [] + for iface in netifaces.interfaces(): + link = None + try: + link = netifaces.ifaddresses(iface)[netifaces.AF_INET][0] + except: + link = None + if link is not None: + ip = link['addr'] + if re.match(r'^((\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])$', + ip) and str(ip) != 'localhost' and str(ip) != '127.0.0.1': + arr.append(ip) + return arr + + @staticmethod + def getHwAddr(ifname): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', ifname[:15])) + return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] + + @staticmethod + def getHwAddr(ifname): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', ifname[:15])) + return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] + + @staticmethod + def mac_addresses(): + mac = get_mac() + ':'.join(("%012X" % mac)[i:i + 2] for i in range(0, 12, 2)) + arr = [] + for iface in psutil.net_io_counters(pernic=True): + try: + addr_list = psutil.net_if_addrs() + mac = addr_list[str(iface)][2][1] + if re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", mac.lower()) and str( + mac) != '00:00:00:00:00:00': + arr.append(mac.lower()) + except Exception as e: + pass + + return arr + + @staticmethod + def screen_info_json_obj(info): + label_list = [ + # 'Identifier', + 'ModelName', 'VendorName', 'Monitor Manufactured', 'DisplaySize', + # 'Gamma', + # 'Horizsync', + # 'VertRefresh' + ] + data = dict() + + for line in info.splitlines(): + line = line.strip().replace('"', '') + intersection = list(set(line.split(' ')).intersection(label_list)) + if len(intersection) > 0: + data[str(intersection[0])] = line.split(intersection[0])[1].strip() + + return data + + @staticmethod + def monitors(): + edid_list = glob.glob('/sys/class/drm/*/edid') + + monitor_list = list() + for edid in edid_list: + result_code, p_out, p_err = Util.execute('parse-edid < {0}'.format(edid)) + + if result_code == 0: + monitor_list.append(System.Hardware.screen_info_json_obj(p_out)) + + return monitor_list + + @staticmethod + def screens(): + result_code, p_out, p_err = Util.execute('xrandr') + arr = [] + if result_code == 0: + for line in p_out.splitlines(): + if len(list(set(line.split(' ')).intersection(['connected']))) > 0: + arr.append(line) + return arr + + @staticmethod + def usb_devices(): + result_code, p_out, p_err = Util.execute('lsusb') + arr = [] + if result_code == 0: + for line in p_out.splitlines(): + if ':' in line and 'Device 001' not in line.split(':')[0]: + arr.append(line) + return arr + + @staticmethod + def printers(): + result_code, p_out, p_err = Util.execute('lpstat -a') + arr = None + if result_code == 0: + arr = p_out.splitlines() + return arr + + @staticmethod + def system_definitions(): + result_code, p_out, p_err = Util.execute('dmidecode -t system') + arr = [] + if result_code == 0: + for line in p_out.splitlines(): + line = line.strip() + if len(list(set(line.split(' ')).intersection(['Manufacturer:', 'Product']))) > 0: + arr.append(line) + return arr + + @staticmethod + def machine_model(): + try: + result_code, p_out, p_err = Util.execute('sudo dmidecode --string system-version') + return str(p_out) + except: + raise + + @staticmethod + def machine_type(): + config = configparser.ConfigParser() + config._interpolation = configparser.ExtendedInterpolation() + config.read(System.Ahenk.config_path()) + return config.get('MACHINE', 'type') + + @staticmethod + def interfaces_details(): + return psutil.net_if_addrs() + + @staticmethod + def ip_addresses(): + arr = [] + for iface in psutil.net_io_counters(pernic=True): + ip = psutil.net_if_addrs()[str(iface)][0][1] + if re.match(r'^((\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])$', ip) and str( + ip) != 'localhost' and str(ip) != '127.0.0.1': + arr.append(ip) + return arr + + class Cpu(object): + + @staticmethod + def times(): + return psutil.cpu_times() + + @staticmethod + def architecture(): + return platform.processor() + + @staticmethod + def physical_core_count(): + return psutil.cpu_count(logical=False) + + @staticmethod + def logical_core_count(): + return psutil.cpu_count(logical=True) + + @staticmethod + def stats(): + return psutil.cpu_stats() + + @staticmethod + def vendor(): + return cpuinfo.get_cpu_info()['vendor_id'] + + @staticmethod + def brand(): + return cpuinfo.get_cpu_info()['brand'] + + @staticmethod + def hz_advertised(): + return cpuinfo.get_cpu_info()['hz_advertised'] + + @staticmethod + def hz_actual(): + return cpuinfo.get_cpu_info()['hz_actual'] + + @staticmethod + def bit(): + return cpuinfo.get_cpu_info()['bits'] + + @staticmethod + def family(): + return cpuinfo.get_cpu_info()['family'] + + @staticmethod + def model(): + return cpuinfo.get_cpu_info()['model'] diff --git a/usr/share/ahenk/base/task/__init__.py b/usr/share/ahenk/base/task/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/base/task/task_in_queue.py b/usr/share/ahenk/base/task/task_in_queue.py new file mode 100644 index 0000000..0aa0d5a --- /dev/null +++ b/usr/share/ahenk/base/task/task_in_queue.py @@ -0,0 +1,28 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN + +import threading + +from base.task.task_job import TaskJob + + +class TaskInQueue(threading.Thread): + """docstring for TaskInQueue""" + + def __init__(self, in_queue): + super(TaskInQueue, self).__init__() + self.in_queue = in_queue + + def run(self): + # Add task to db. Adding task to db important because task can be lost when processing. + # Call plugin manager and process message inside task job + try: + task = self.in_queue.get() + print(task) + # Can be validate task before processing + job = TaskJob(task) + job.start() + + except Exception as e: + raise diff --git a/usr/share/ahenk/base/task/task_job.py b/usr/share/ahenk/base/task/task_job.py new file mode 100644 index 0000000..13ba998 --- /dev/null +++ b/usr/share/ahenk/base/task/task_job.py @@ -0,0 +1,19 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN +import threading + +from base.scope import Scope + + +class TaskJob(threading.Thread): + """docstring for TaskJob""" + + def __init__(self, task): + super(TaskJob, self).__init__() + scope = Scope.get_instance() + self.task = task + self.pluginManager = scope.get_plugin_manager() + + def run(self): + self.pluginManager.process(self.task) diff --git a/usr/share/ahenk/base/task/task_manager.py b/usr/share/ahenk/base/task/task_manager.py new file mode 100644 index 0000000..2cd1e5b --- /dev/null +++ b/usr/share/ahenk/base/task/task_manager.py @@ -0,0 +1,80 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Author: İsmail BAŞARAN + +from base.scope import Scope +from base.model.message_factory import MessageFactory +from base.model.enum.message_type import MessageType +import json + + +class TaskManager(object): + """docstring for TaskManager""" + + def __init__(self): + # super(TaskManager, self).__init__() + scope = Scope.get_instance() + self.pluginManager = scope.get_plugin_manager() + self.logger = scope.get_logger() + self.db_service = scope.get_db_service() + self.scheduler = scope.get_scheduler() + + def addTask(self, task): + try: + self.saveTask(task) + if task.get_cron_str() is None or task.get_cron_str() == '': + self.logger.debug('Adding task ... ') + self.pluginManager.process_task(task) + else: + self.scheduler.save_and_add_job(task) + + except Exception as e: + self.logger.debug('Exception occurred when adding task. Error Message: {0}'.format(str(e))) + + def addPolicy(self, policy): + try: + self.pluginManager.process_policy(policy) + except Exception as e: + self.logger.error("Exception occurred when adding policy. Error Message: {0}".format(str(e))) + + def saveTask(self, task): + try: + self.logger.debug('task save') + task_cols = ['id', 'create_date', 'modify_date', 'command_cls_id', 'parameter_map', 'deleted', 'plugin', + 'cron_expr', 'file_server'] + plu_cols = ['active', 'create_date', 'deleted', 'description', 'machine_oriented', 'modify_date', 'name', + 'policy_plugin', 'user_oriented', 'version', 'task_plugin', 'x_based'] + plugin_args = [str(task.get_plugin().get_active()), str(task.get_plugin().get_create_date()), + str(task.get_plugin().get_deleted()), str(task.get_plugin().get_description()), + str(task.get_plugin().get_machine_oriented()), str(task.get_plugin().get_modify_date()), + str(task.get_plugin().get_name()), str(task.get_plugin().get_policy_plugin()), + str(task.get_plugin().get_user_oriented()), str(task.get_plugin().get_version()), + str(task.get_plugin().get_task_plugin()), str(task.get_plugin().get_x_based())] + plugin_id = self.db_service.update('plugin', plu_cols, plugin_args) + + params = json.dumps(task.get_parameter_map()) + + values = [str(task.get_id()), str(task.get_create_date()), str(task.get_modify_date()), + str(task.get_command_cls_id()), str(params), str(task.get_deleted()), + str(plugin_id), str(task.get_cron_str()), str(task.get_file_server())] + self.db_service.update('task', task_cols, values) + except Exception as e: + self.logger.error("Exception occurred while saving task. Error Message: {0}".format(str(e))) + + def updateTask(self, task): + # TODO not implemented yet + # This is updates task status processing - processed ... + pass + + def deleteTask(self, task): + # TODO not implemented yet + # remove task if it is processed + pass + + def sendMessage(self, type, message): + # TODO not implemented yet + pass + + +if __name__ == '__main__': + print(MessageFactory.createMessage(MessageType.TASK_PROCESSING, "my message")) diff --git a/usr/share/ahenk/base/timer/setup_timer.py b/usr/share/ahenk/base/timer/setup_timer.py new file mode 100644 index 0000000..bfd3995 --- /dev/null +++ b/usr/share/ahenk/base/timer/setup_timer.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +class SetupTimer: + @staticmethod + def start(timer): + timer.setDaemon(True) + timer.start() diff --git a/usr/share/ahenk/base/timer/timer.py b/usr/share/ahenk/base/timer/timer.py new file mode 100644 index 0000000..28a592f --- /dev/null +++ b/usr/share/ahenk/base/timer/timer.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import time +import threading + + +class Timer(threading.Thread): + def __init__(self, timeout, timeout_function, checker_func=None, checker_success_function=None, **kwargs): + threading.Thread.__init__(self) + self.timeout = int(timeout) + self.timeout_function = timeout_function + self.timeout_function_args = None + self.checker_func_args = None + self.checker_success_function_args = None + + if kwargs is not None and kwargs['kwargs'] is not None: + if 'timeout_args' in kwargs['kwargs']: + self.timeout_function_args = kwargs['kwargs']['timeout_args'] + + if 'checker_args' in kwargs['kwargs']: + self.checker_func_args = kwargs['kwargs']['checker_args'] + + if 'success_args' in kwargs['kwargs']: + self.checker_success_function_args = kwargs['kwargs']['success_args'] + + self.checker_func = checker_func + self.checker_success_function = checker_success_function + + def run_function(self, function, parameter=None): + if function is not None: + if parameter is None: + function() + else: + function(parameter) + + def run(self): + timer = self.timeout + + while timer > 0: + + if self.checker_func is not None: + if self.checker_func_args is not None: + if self.checker_func(self.checker_func_args) is True: + self.run_function(self.checker_success_function, self.checker_success_function_args) + return + else: + if self.checker_func() is True: + self.run_function(self.checker_success_function, self.checker_success_function_args) + return + + time.sleep(1) + timer -= 1 + if self.timeout_function_args is not None: + self.timeout_function(self.timeout_function_args) + else: + self.timeout_function() diff --git a/usr/share/ahenk/base/util/util.py b/usr/share/ahenk/base/util/util.py new file mode 100644 index 0000000..726eb4d --- /dev/null +++ b/usr/share/ahenk/base/util/util.py @@ -0,0 +1,404 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Author: Volkan Şahin + +import datetime +import grp +import hashlib +import json +import os +import pwd +import shutil +import stat +import subprocess +import uuid +import locale +from base.scope import Scope + + +class Util: + + + def __init__(self): + super().__init__() + + @staticmethod + def get_ask_path_file(): + return '/usr/share/ahenk/base/agreement/' + + @staticmethod + def close_session(username): + Util.execute('pkill -9 -u {0}'.format(username)) + + @staticmethod + def shutdown(): + Util.execute('reboot') + + @staticmethod + def create_file(full_path): + try: + if os.path.exists(full_path): + return None + else: + file = open(full_path, 'w') + file.close() + return True + except: + raise + + @staticmethod + def delete_folder(full_path): + try: + shutil.rmtree(full_path) + except: + raise + + @staticmethod + def delete_file(full_path): + try: + if Util.is_exist(full_path): + os.remove(full_path) + except: + raise + + @staticmethod + def rename_file(old_full_path, new_full_path): + try: + os.rename(old_full_path, new_full_path) + except: + raise + + @staticmethod + def copy_file(source_full_path, destination_full_path): + try: + shutil.copy2(source_full_path, destination_full_path) + except: + raise + + @staticmethod + def move(source_full_path, destination_full_path): + try: + shutil.move(source_full_path, destination_full_path) + except: + raise + + @staticmethod + def get_size(full_path): + # byte + try: + return os.path.getsize(full_path) + except: + raise + + @staticmethod + def link_path(source_path, destination_path): + try: + os.symlink(source_path, destination_path) + except: + raise + + @staticmethod + def read_file(full_path, mode='r'): + content = None + try: + with open(full_path, mode) as f: + content = f.read() + except: + raise + finally: + return content + + @staticmethod + def read_file_by_line(full_path, mode='r'): + line_list = list() + with open(full_path, mode) as f: + lines = f.readlines() + for line in lines: + line_list.append(line) + return line_list + + @staticmethod + def write_file(full_path, content, mode='w+'): + file = None + try: + file = open(full_path, mode) + file.write(content) + except: + raise + finally: + file.close() + + @staticmethod + def make_executable(full_path): + try: + st = os.stat(full_path) + os.chmod(full_path, st.st_mode | stat.S_IEXEC) + except: + raise + + @staticmethod + def change_owner(full_path, user_name=None, group_name=None): + try: + shutil.chown(full_path, user_name, group_name) + except: + raise + + @staticmethod + def execute(command, stdin=None, env=None, cwd=None, shell=True, result=True, as_user=None, ip=None): + + try: + if ip: + command = 'ssh root@{0} "{1}"'.format(ip, command) + Scope.get_instance().get_logger().debug('Executing command: ' +str(command)) + + elif as_user: + command = 'su - {0} -c "{1}"'.format(as_user, command) + Scope.get_instance().get_logger().debug('Executing command: ' +str(command)) + process = subprocess.Popen(command, stdin=stdin, env=env, cwd=cwd, stderr=subprocess.PIPE, + stdout=subprocess.PIPE, shell=shell) + + Scope.get_instance().get_logger().debug('Executing command: ' + str(command)) + + if result is True: + result_code = process.wait() + p_out = process.stdout.read().decode("unicode_escape") + p_err = process.stderr.read().decode("unicode_escape") + + return result_code, p_out, p_err + else: + return None, None, None + except Exception as e: + return 1, 'Could not execute command: {0}. Error Message: {1}'.format(command, str(e)), '' + + @staticmethod + def scopy_from_remote(source_path, destination_path, ip): + command = 'scp -r root@' + ip + ':' + source_path + ' ' + destination_path + process = subprocess.Popen(command, stderr=subprocess.PIPE,stdout=subprocess.PIPE, shell=True) + result_code = process.wait() + p_out = process.stdout.read().decode("unicode_escape") + p_err = process.stderr.read().decode("unicode_escape") + + return result_code, p_out, p_err + + @staticmethod + def execute_script(script_path, parameters=None): + command = [] + if os.path.exists(script_path): + command.append(script_path) + else: + raise Exception('[Util] Script is required') + if parameters is not None: + for p in parameters: + command.append(p) + + return subprocess.check_call(command) + + @staticmethod + def is_exist(full_path): + try: + return os.path.exists(full_path) + except: + raise + + @staticmethod + def create_directory(dir_path): + try: + return os.makedirs(dir_path) + except: + raise + + @staticmethod + def string_to_json(string): + try: + return json.loads(string) + except: + raise + + # TODO json abilities + + + @staticmethod + def file_owner(full_path): + try: + st = os.stat(full_path) + uid = st.st_uid + return pwd.getpwuid(uid)[0] + except: + raise + + @staticmethod + def file_group(full_path): + try: + st = os.stat(full_path) + gid = st.st_uid + return grp.getgrgid(gid)[0] + except: + raise + + @staticmethod + def install_with_dpkg(full_path): + command_dpkg = 'dpkg -i {0}' + command_dep = 'apt -f install -y' + commands = [command_dpkg.format(full_path),command_dep] + for cmd in commands: + try: + process = subprocess.Popen(cmd, shell=True) + process.wait() + except: + raise + + @staticmethod + def install_with_apt_get(package_name, package_version=None): + + if package_version is not None: + command = 'apt-get install --yes --force-yes {0}={1}'.format(package_name, package_version) + else: + command = 'apt-get install --yes --force-yes {0}'.format(package_name) + + return Util.execute(command) + + @staticmethod + def uninstall_package(package_name, package_version=None): + + if package_version is not None: + command = 'apt-get purge --yes --force-yes {0}={1}'.format(package_name, package_version) + else: + command = 'apt-get purge --yes --force-yes {0}'.format(package_name) + + return Util.execute(command) + + @staticmethod + def is_installed(package_name): + + result_code, p_out, p_err = Util.execute('dpkg -s {0}'.format(package_name)) + try: + lines = str(p_out).split('\n') + for line in lines: + if len(line) > 1: + if line.split(None, 1)[0].lower() == 'status:': + if 'installed' in line.split(None, 1)[1].lower(): + return True + return False + except Exception as e: + return False + + @staticmethod + def get_md5_file(file_path): + hash_md5 = hashlib.md5() + with open(file_path, 'rb') as f: + for chunk in iter(lambda: f.read(4096), b""): + hash_md5.update(chunk) + return str(hash_md5.hexdigest()) + + @staticmethod + def get_md5_text(content): + hash_md5 = hashlib.md5() + hash_md5.update(content.encode()) + return str(hash_md5.hexdigest()) + + @staticmethod + def timestamp(): + return str(datetime.datetime.now().strftime("%d-%m-%Y %I:%M")) + + @staticmethod + def generate_uuid(): + return str(uuid.uuid4()) + + @staticmethod + def get_language(): + locale_info = locale.getdefaultlocale() + return locale_info[0] + + @staticmethod + def set_permission(path, permission_code): + Util.execute('chmod -R {0} {1}'.format(permission_code, path)) + + @staticmethod + def has_attr_json(arg, attr_name): + for j in json.loads(json.dumps(arg)): + if attr_name in j: + return True + return False + + @staticmethod + def remove_package(package_name, package_version): + command = "sudo apt-get --yes --force-yes purge {0}={1}".format(package_name, package_version) + result_code, p_out, p_err = Util.execute(command) + return result_code, p_out, p_err + + @staticmethod + def send_notify(title, body, display, user, icon=None, timeout=5000): + + inner_command = 'notify-send "{0}" "{1}" -t {2}'.format(title, body, timeout) + if icon: + inner_command += ' -i {0}'.format(icon) + + if user != 'root': + Util.execute('export DISPLAY={0}; su - {1} -c \'{2}\''.format(display, user, inner_command)) + + @staticmethod + def show_message(username,display=':0',message='', title=''): + ask_path = Util.get_ask_path_file()+ 'confirm.py' + try: + + if username is not None: + command = 'export DISPLAY={0};su - {1} -c \'python3 {2} \"{3}\" \"{4}\"\''.format(display, username, + ask_path, + message, + title) + result_code, p_out, p_err = Util.execute(command) + + if p_out.strip() == 'Y': + return True + elif p_out.strip() == 'N': + return False + else: + return None + + else: + return None + except Exception as e : + print("Error when showing message " + str(e)) + + return None; + + + + @staticmethod + def show_registration_message(login_user_name,message,title,host=None): + + ask_path = Util.get_ask_path_file()+ 'ahenkmessage.py' + + display_number = ":0" + + if host is None: + command = 'export DISPLAY={0}; su - {1} -c \"python3 {2} \'{3}\' \'{4}\' \"'.format(display_number, login_user_name, + ask_path, message, title) + else: + command = 'export DISPLAY={0}; su - {1} -c \"python3 {2} \'{3}\' \'{4}\' \'{5}\' \"'.format(display_number, + login_user_name, + ask_path, + message, title, + host) + result_code, p_out, p_err = Util.execute(command) + + pout = str(p_out).replace('\n', '') + + return pout + + @staticmethod + def show_unregistration_message(login_user_name,display_number,message,title): + + ask_path = Util.get_ask_path_file()+ 'unregistrationmessage.py' + + command = 'export DISPLAY={0}; su - {1} -c \"python3 {2} \'{3}\' \'{4}\' \"'.format(display_number, + login_user_name, + ask_path, + message, title + ) + result_code, p_out, p_err = Util.execute(command) + + pout = str(p_out).replace('\n', '') + + return pout + diff --git a/usr/share/ahenk/helper/__init__.py b/usr/share/ahenk/helper/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/share/ahenk/helper/system.py b/usr/share/ahenk/helper/system.py new file mode 100644 index 0000000..d76c95f --- /dev/null +++ b/usr/share/ahenk/helper/system.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os, signal + +class ProcEntry(object): + + def __init__(self, name, pid, cmdline, euid, egid): + super(ProcEntry, self).__init__() + self.name = name + self.pid = pid + self.cmdline = cmdline + self.euid = euid + self.egid = egid + + def __str__(self): + return 'name: {0}, pid: {1}, uid: {2}, gid: {3}, cmd: {4}' \ + .format(self.name, self.pid, self.euid, self.egid, self.cmdline) + + +class ProcParseError(Exception): + + def __init__(self, msg): + super(Exception, self).__init__(msg) + + +def proclist(): + def raise_if_less(l, n): + if len(l) < n: + raise ProcParseError('too few fields, expected at least ' + str(n)) + + for pid in [pid for pid in os.listdir('/proc') if pid.isdigit()]: + p = os.path.join('/proc', pid, 'cmdline') + cmdline = open(p).read() + p = os.path.join('/proc', pid, 'status') + euid = None + egid = None + name = None + for lin in open(p): + if lin.startswith('Name:'): + s = lin.split() + raise_if_less(s, 2) + name = s[1] + elif lin.startswith('Uid:'): + uid_line = lin.split() + raise_if_less(uid_line, 3) + euid = int(uid_line[2]) + elif lin.startswith('Gid:'): + gid_line = lin.split() + raise_if_less(gid_line, 3) + egid = int(gid_line[2]) + + yield ProcEntry(name, int(pid), cmdline, euid, egid) + +PATH_SHELLS='/etc/shells' + +def login_shells(): + valid = lambda s: s.rstrip(' \n') and not s.lstrip(' \t').startswith('#') + return [lin.rstrip('\n') for lin in open(PATH_SHELLS).readlines() + if valid(lin)] + +def shell_is_interactive(sh): + shells = ['sh', 'bash', 'dash', 'zsh', 'fish', 'ksh', 'csh', 'tcsh'] + return any(s == os.path.basename(sh) for s in shells) + +def killuserprocs(uid): + for p in proclist(): + if p.euid == uid: + try: + os.kill(p.pid, signal.SIGTERM) + except ProcessLookupError as e: + # The process might have died immediately, up till now, even + # before we had a chance to send a signal to it. + pass diff --git a/usr/share/libpam-script/pam_script_ses_close b/usr/share/libpam-script/pam_script_ses_close new file mode 100755 index 0000000..f5bab47 --- /dev/null +++ b/usr/share/libpam-script/pam_script_ses_close @@ -0,0 +1,30 @@ +#!/bin/bash + +function error_exit() { + log "$1:$2 failed, exit status $?" + exit 1 +} + +trap 'error_exit "${BASH_SOURCE}" "${LINENO}"' ERR + +LOG=/var/log/pam_script.log + +function log() { + logger --priority auth.info --tag "$0" "$@" + echo "$(date) $0: $@" >> $LOG +} + +if [ -n $PAM_USER ] && [ $PAM_USER != "root" ]; then + if ([ -n $PAM_SERVICE ] && [[ $PAM_SERVICE == *"dm" ]]) || ([ -n $PAM_TTY ] && [[ $PAM_TTY == ":"* ]]); then + SERVICE="none" + if [ -n $PAM_SERVICE ]; then + SERVICE="$PAM_SERVICE" + fi + TTY_DISPLAY="none" + if [ -n $PAM_TTY ]; then + TTY_DISPLAY="$PAM_TTY" + fi + log "logout: $PAM_USER service: $SERVICE tty: $TTY_DISPLAY" + sudo python3 /usr/share/ahenk/ahenkd.py logout $PAM_USER + fi +fi diff --git a/usr/share/libpam-script/pam_script_ses_open b/usr/share/libpam-script/pam_script_ses_open new file mode 100755 index 0000000..3512806 --- /dev/null +++ b/usr/share/libpam-script/pam_script_ses_open @@ -0,0 +1,30 @@ +#!/bin/bash + +function error_exit() { + log "$1:$2 failed, exit status $?" + exit 1 +} + +trap 'error_exit "${BASH_SOURCE}" "${LINENO}"' ERR + +LOG=/var/log/pam_script.log + +function log() { + logger --priority auth.info --tag "$0" "$@" + echo "$(date) $0: $@" >> $LOG +} + +if [ -n $PAM_USER ] && [ $PAM_USER != "root" ]; then + if ([ -n $PAM_SERVICE ] && [[ $PAM_SERVICE == *"dm" ]]) || ([ -n $PAM_TTY ] && [[ $PAM_TTY == ":"* ]]); then + SERVICE="none" + if [ -n $PAM_SERVICE ]; then + SERVICE="$PAM_SERVICE" + fi + TTY_DISPLAY="none" + if [ -n $PAM_TTY ]; then + TTY_DISPLAY="$PAM_TTY" + fi + log "login: $PAM_USER service: $SERVICE tty: $TTY_DISPLAY" + sudo python3 /usr/share/ahenk/ahenkd.py login $PAM_USER $SERVICE $TTY_DISPLAY + fi +fi