2017-11-06 23:58:01 +00:00
|
|
|
# coding=utf-8
|
|
|
|
from __future__ import absolute_import
|
|
|
|
|
2017-11-26 22:14:46 +00:00
|
|
|
import octoprint.plugin
|
2017-11-07 23:35:38 +00:00
|
|
|
from octoprint.users import FilebasedUserManager, User
|
2017-11-06 23:58:01 +00:00
|
|
|
from octoprint.settings import settings
|
|
|
|
import ldap
|
|
|
|
import uuid
|
|
|
|
|
|
|
|
|
2017-11-26 22:14:46 +00:00
|
|
|
class LDAPUserManager(FilebasedUserManager,
|
|
|
|
octoprint.plugin.SettingsPlugin,
|
|
|
|
octoprint.plugin.TemplatePlugin):
|
|
|
|
|
2017-11-07 23:35:38 +00:00
|
|
|
#Login phase :
|
|
|
|
# - findUser called, if it return a user
|
|
|
|
# - chaeckPassword called, if it return True
|
|
|
|
# - login_user called with User returned by previous findUser
|
|
|
|
|
2017-11-06 23:58:01 +00:00
|
|
|
def checkPassword(self, username, password):
|
|
|
|
try:
|
2017-11-08 09:23:23 +00:00
|
|
|
connection = self.getLDAPClient()
|
2017-11-07 23:35:38 +00:00
|
|
|
|
2017-11-08 09:23:23 +00:00
|
|
|
username = self.escapeLDAP(username)
|
|
|
|
dn = self.findLDAPUser(username)
|
2018-01-27 22:43:38 +00:00
|
|
|
if dn is None:
|
|
|
|
return False
|
2017-11-06 23:58:01 +00:00
|
|
|
connection.bind_s(dn, password)
|
2017-11-07 23:35:38 +00:00
|
|
|
connection.unbind_s()
|
2017-11-06 23:58:01 +00:00
|
|
|
|
2017-11-07 23:35:38 +00:00
|
|
|
user = FilebasedUserManager.findUser(self, username)
|
2017-11-06 23:58:01 +00:00
|
|
|
if not user:
|
2017-11-08 09:23:23 +00:00
|
|
|
self._logger.debug("Add new user")
|
2017-11-07 23:35:38 +00:00
|
|
|
self.addUser(username, str(uuid.uuid4()), True)
|
2017-11-06 23:58:01 +00:00
|
|
|
return True
|
2017-11-08 09:23:23 +00:00
|
|
|
|
2017-11-06 23:58:01 +00:00
|
|
|
except ldap.INVALID_CREDENTIALS:
|
2017-11-07 23:35:38 +00:00
|
|
|
self._logger.error("LDAP : Your username or password is incorrect.")
|
2017-11-06 23:58:01 +00:00
|
|
|
return FilebasedUserManager.checkPassword(self, username, password)
|
|
|
|
except ldap.LDAPError, e:
|
|
|
|
if type(e.message) == dict:
|
|
|
|
for (k, v) in e.message.iteritems():
|
2017-11-07 23:35:38 +00:00
|
|
|
self._logger.error("%s: %sn" % (k, v))
|
2017-11-06 23:58:01 +00:00
|
|
|
else:
|
2017-11-07 23:35:38 +00:00
|
|
|
self._logger.error(e.message)
|
2017-11-06 23:58:01 +00:00
|
|
|
return False
|
|
|
|
|
2017-11-07 23:35:38 +00:00
|
|
|
def changeUserPassword(self, username, password):
|
2017-11-08 09:23:23 +00:00
|
|
|
#Changing password of LDAP users is not allowed
|
|
|
|
if FilebasedUserManager.findUser(self, username) is not None:
|
|
|
|
return FilebasedUserManager.changeUserPassword(self, username, password)
|
2017-11-07 23:35:38 +00:00
|
|
|
|
|
|
|
def findUser(self, userid=None, session=None):
|
|
|
|
local_user = FilebasedUserManager.findUser(self, userid, session)
|
|
|
|
#If user not exists in local database, search it on LDAP
|
|
|
|
if userid and not local_user:
|
2017-11-08 09:23:23 +00:00
|
|
|
if(self.findLDAPUser(userid)):
|
2017-11-07 23:35:38 +00:00
|
|
|
#Return a fake user instance
|
|
|
|
return User(userid, str(uuid.uuid4()), True, ["user"])
|
|
|
|
|
2017-11-08 09:23:23 +00:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
2017-11-07 23:35:38 +00:00
|
|
|
else :
|
2017-11-08 09:23:23 +00:00
|
|
|
self._logger.debug("Local user found")
|
2017-11-07 23:35:38 +00:00
|
|
|
return local_user
|
2017-11-06 23:58:01 +00:00
|
|
|
|
2017-11-08 09:23:23 +00:00
|
|
|
def findLDAPUser(self, userid):
|
|
|
|
ldap_search_base = settings().get(["accessControl", "ldap_search_base"])
|
2018-01-27 22:43:38 +00:00
|
|
|
groups = settings().get(["accessControl", "groups"])
|
2017-11-08 09:23:23 +00:00
|
|
|
userid = self.escapeLDAP(userid)
|
|
|
|
|
|
|
|
if ldap_search_base is None:
|
|
|
|
self._logger.error("LDAP conf error")
|
|
|
|
return None
|
|
|
|
|
|
|
|
try:
|
|
|
|
connection = self.getLDAPClient()
|
|
|
|
|
2018-01-27 22:43:38 +00:00
|
|
|
#verify user)
|
2017-11-08 09:23:23 +00:00
|
|
|
result = connection.search_s(ldap_search_base, ldap.SCOPE_SUBTREE, "uid=" + userid)
|
2018-01-27 22:43:38 +00:00
|
|
|
if result is None or len(result) == 0:
|
2017-11-08 09:23:23 +00:00
|
|
|
return None
|
2018-01-27 22:43:38 +00:00
|
|
|
self._logger.error("LDAP-AUTH: User found!")
|
|
|
|
|
|
|
|
#check group(s)
|
|
|
|
if groups is not None:
|
|
|
|
self._logger.error("LDAP-AUTH: Checking Groups...")
|
|
|
|
group_filter = ""
|
|
|
|
if "," in groups:
|
|
|
|
group_list = groups.split(",")
|
|
|
|
group_filter = "(|"
|
|
|
|
for g in group_list:
|
|
|
|
group_filter = group_filter + "(cn=%s)" % g
|
|
|
|
group_filter = group_filter + ")"
|
|
|
|
else:
|
|
|
|
group_filter = "(cn=%s)" % groups
|
|
|
|
|
|
|
|
query = "(&(objectClass=posixGroup)%s(memberUid=%s))" % (group_filter, userid)
|
|
|
|
self._logger.error("LDAP-AUTH QUERY:" + query)
|
|
|
|
group_result = connection.search_s(ldap_search_base, ldap.SCOPE_SUBTREE, query)
|
|
|
|
|
|
|
|
if group_result is None or len(group_result) == 0:
|
|
|
|
print("LDAP-AUTH: Group not found")
|
|
|
|
return None
|
|
|
|
|
|
|
|
self._logger.error("LDAP-AUTH: Group matched!")
|
|
|
|
|
|
|
|
#disconnect
|
|
|
|
connection.unbind_s()
|
2017-11-08 09:23:23 +00:00
|
|
|
|
|
|
|
#Get the DN of first user found
|
|
|
|
dn, data = result[0]
|
|
|
|
return dn
|
|
|
|
|
2018-01-27 22:43:38 +00:00
|
|
|
except ldap.NO_SUCH_OBJECT:
|
|
|
|
self._logger.error("LDAP-AUTH: NO_SUCH_OBJECT")
|
|
|
|
return None
|
|
|
|
|
2017-11-08 09:23:23 +00:00
|
|
|
except ldap.LDAPError, e:
|
|
|
|
if type(e.message) == dict:
|
|
|
|
for (k, v) in e.message.iteritems():
|
|
|
|
self._logger.error("%s: %sn" % (k, v))
|
|
|
|
else:
|
|
|
|
self._logger.error(e.message)
|
|
|
|
return None
|
|
|
|
|
|
|
|
def escapeLDAP(self, str):
|
|
|
|
reservedStrings = ['+','=','\\','\r','\n','#',',','>','<','"',';']
|
|
|
|
for ch in reservedStrings:
|
|
|
|
if ch in str:
|
|
|
|
str = str.replace(ch, '\\' + ch)
|
|
|
|
return str
|
|
|
|
|
|
|
|
def getLDAPClient(self):
|
|
|
|
ldap_server = settings().get(["accessControl", "ldap_uri"])
|
|
|
|
ldap_verifypeer = settings().get(["accessControl", "ldap_tls_reqcert"])
|
|
|
|
if ldap_server is None:
|
|
|
|
self._logger.error("LDAP conf error")
|
|
|
|
Exception("LDAP conf error, server is missing")
|
|
|
|
|
|
|
|
connection = ldap.initialize(ldap_server)
|
|
|
|
if (ldap_server.startswith('ldaps://')):
|
|
|
|
verifypeer = ldap.OPT_X_TLS_NEVER
|
|
|
|
if ldap_verifypeer == 'demand':
|
|
|
|
verifypeer = ldap.OPT_X_TLS_DEMAND
|
|
|
|
connection.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, verifypeer)
|
|
|
|
try:
|
|
|
|
connection.start_tls_s()
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
return connection
|
|
|
|
|
2017-11-26 22:14:46 +00:00
|
|
|
# Softwareupdate hook
|
|
|
|
|
|
|
|
def get_update_information(self):
|
|
|
|
return dict(
|
|
|
|
filamentmanager=dict(
|
|
|
|
displayName="Auth LDAP",
|
|
|
|
displayVersion=self._plugin_version,
|
|
|
|
|
|
|
|
# version check: github repository
|
|
|
|
type="github_release",
|
|
|
|
user="gillg",
|
|
|
|
repo="OctoPrint-LDAP",
|
|
|
|
current=self._plugin_version,
|
|
|
|
|
|
|
|
# update method: pip
|
|
|
|
pip="https://github.com/gillg/OctoPrint-LDAP/archive/{target_version}.zip"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# UserManager hook
|
|
|
|
|
|
|
|
def ldap_user_factory(components, settings, *args, **kwargs):
|
|
|
|
return LDAPUserManager()
|
|
|
|
|
|
|
|
# SettingsPlugin
|
|
|
|
|
|
|
|
def get_settings_defaults(self):
|
|
|
|
return dict(
|
|
|
|
accessControl=dict(
|
|
|
|
ldap_uri=None,
|
|
|
|
ldap_tls_reqcert='demand',
|
2018-01-27 22:43:38 +00:00
|
|
|
ldap_search_base=None,
|
|
|
|
groups=None
|
2017-11-26 22:14:46 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# TemplatePlugin
|
|
|
|
|
|
|
|
def get_template_configs(self):
|
|
|
|
return [
|
|
|
|
dict(type="settings", template="settings.jinja2")
|
|
|
|
]
|
|
|
|
|
2017-11-06 23:58:01 +00:00
|
|
|
|
|
|
|
__plugin_name__ = "Auth LDAP"
|
|
|
|
|
|
|
|
def __plugin_load__():
|
2017-11-26 22:14:46 +00:00
|
|
|
global __plugin_implementation__
|
|
|
|
__plugin_implementation__ = LDAPUserManager()
|
|
|
|
|
2017-11-06 23:58:01 +00:00
|
|
|
global __plugin_hooks__
|
|
|
|
__plugin_hooks__ = {
|
2017-11-26 22:14:46 +00:00
|
|
|
"octoprint.users.factory": __plugin_implementation__.ldap_user_factory,
|
|
|
|
"octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information,
|
2017-11-06 23:58:01 +00:00
|
|
|
}
|
2017-11-26 22:14:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
#@TODO Command clean LDAP users deleted
|