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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions octoprint_auth_ldap/templates/settings.jinja2

This file was deleted.

154 changes: 107 additions & 47 deletions octoprint_auth_ldap/__init__.py → octoprint_authldap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from __future__ import absolute_import

import octoprint.plugin
from octoprint.users import FilebasedUserManager, User
from octoprint.users import UserManager, FilebasedUserManager, User
from octoprint.settings import settings
import ldap
import uuid
Expand All @@ -11,11 +11,10 @@
class LDAPUserManager(FilebasedUserManager,
octoprint.plugin.SettingsPlugin,
octoprint.plugin.TemplatePlugin):

#Login phase :
# - findUser called, if it return a user
# - chaeckPassword called, if it return True
# - login_user called with User returned by previous findUser
# Login phase :
# - findUser called, if it return a user
# - checkPassword called, if it return True
# - login_user called with User returned by previous findUser

def checkPassword(self, username, password):
try:
Expand All @@ -31,7 +30,10 @@ def checkPassword(self, username, password):
user = FilebasedUserManager.findUser(self, username)
if not user:
self._logger.debug("Add new user")
self.addUser(username, str(uuid.uuid4()), True)
self.addUser(username,
str(uuid.uuid4()),
active=settings().getBoolean(["plugins", "authldap", "auto_activate"]),
roles=self.getRoles())
return True

except ldap.INVALID_CREDENTIALS:
Expand All @@ -46,22 +48,25 @@ def checkPassword(self, username, password):
return False

def changeUserPassword(self, username, password):
#Changing password of LDAP users is not allowed
# Changing password of LDAP users is not allowed
if FilebasedUserManager.findUser(self, username) is not None:
return FilebasedUserManager.changeUserPassword(self, username, password)

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 user not exists in local database, search it on LDAP
if userid and not local_user:
if(self.findLDAPUser(userid)):
#Return a fake user instance
return User(userid, str(uuid.uuid4()), True, ["user"])
if (self.findLDAPUser(userid)):
# Return a fake user instance
return User(userid,
str(uuid.uuid4()),
settings().getBoolean(["plugins", "authldap", "auto_activate"]),
self.getRoles())

else:
return None

else :
else:
self._logger.debug("Local user found")
return local_user

Expand All @@ -77,13 +82,13 @@ def findLDAPUser(self, userid):
try:
connection = self.getLDAPClient()

#verify user)
# verify user)
result = connection.search_s(ldap_search_base, ldap.SCOPE_SUBTREE, "uid=" + userid)
if result is None or len(result) == 0:
return None
self._logger.error("LDAP-AUTH: User found!")

#check group(s)
# check group(s)
if groups is not None:
self._logger.error("LDAP-AUTH: Checking Groups...")
group_filter = ""
Expand All @@ -106,58 +111,92 @@ def findLDAPUser(self, userid):

self._logger.error("LDAP-AUTH: Group matched!")

#disconnect
# disconnect
connection.unbind_s()

#Get the DN of first user found
# Get the DN of first user found
dn, data = result[0]
return dn

except ldap.NO_SUCH_OBJECT:
self._logger.error("LDAP-AUTH: NO_SUCH_OBJECT")
return None

except ldap.SERVER_DOWN:
self._logger.debug("LDAP-AUTH: Server unreachable!")

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
return None

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")
self._logger.debug("Creating LDAP Client")
ldap_server = settings().get(["plugins", "authldap", "ldap_uri"])
self._logger.debug("LDAP URL %s" % ldap_server)
if not ldap_server:
self._logger.debug("UserManager: %s" % settings().get(["accessControl", "userManager"]))
raise 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':
connection.set_option(ldap.OPT_REFERRALS, 0)
self._logger.debug("LDAP initialized")

method = settings().get(["plugins", "authldap", "ldap_method"])
if (ldap_server.startswith('ldaps://') or method == 'TLS'):
self._logger.debug("LDAP is using TLS, setting ldap options...")
ldap_verifypeer = settings().get(
["plugins", "authldap", "ldap_tls_reqcert"])

verifypeer = ldap.OPT_X_TLS_HARD
if ldap_verifypeer == 'NEVER':
verifypeer = ldap.OPT_X_TLS_NEVER
elif ldap_verifypeer == 'ALLOW':
verifypeer = ldap.OPT_X_TLS_ALLOW
elif ldap_verifypeer == 'TRY':
verifypeer = ldap.OPT_X_TLS_TRY
elif ldap_verifypeer == 'DEMAND':
verifypeer = ldap.OPT_X_TLS_DEMAND
connection.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, verifypeer)
try:
connection.start_tls_s()
self._logger.error("TLS connection established.")
except:
self._logger.error("Error initializing tls connection")
pass

masterLogin = settings().get(["plugins", "authldap", "ldap_master_user"])
masterPassword = settings().get(["plugins", "authldap", "ldap_master_password"])
if (masterLogin and masterPassword):
connection.simple_bind_s(masterLogin, masterPassword)
connection.unbind_s()

return connection

def escapeLDAP(self, str):
reservedStrings = ['+', '=', '\\', '\r',
'\n', '#', ',', '>', '<', '"', ';']
for ch in reservedStrings:
if ch in str:
str = str.replace(ch, '\\' + ch)
return str

def getRoles(self):
defaultRoles = []
roles = settings().get(["plugins", "authldap", "roles"])
if roles is not None:
defaultRoles = [x.strip() for x in roles.split(',')]
return defaultRoles

# Softwareupdate hook

def get_update_information(self):
return dict(
filamentmanager=dict(
displayName="Auth LDAP",
authldap=dict(
displayName="AuthLDAP",
displayVersion=self._plugin_version,

# version check: github repository
Expand All @@ -167,7 +206,8 @@ def get_update_information(self):
current=self._plugin_version,

# update method: pip
pip="https://github.com/gillg/OctoPrint-LDAP/archive/{target_version}.zip"
pip=("https://github.com"
"/gillg/OctoPrint-LDAP/archive/{target_version}.zip")
)
)

Expand All @@ -180,33 +220,53 @@ def ldap_user_factory(components, settings, *args, **kwargs):

def get_settings_defaults(self):
return dict(
accessControl=dict(
ldap_uri=None,
ldap_tls_reqcert='demand',
ldap_search_base=None,
groups=None
)
ldap_uri=None,
ldap_search_base=None,
ldap_method=None,
auto_activate=True,
roles="user",
groups=None,
ldap_tls_reqcert=None,
ldap_master_user=None,
ldap_master_password=None
)

def on_settings_save(self, data):
old_flag = self._settings.get_boolean(["active"])
octoprint.plugin.SettingsPlugin.on_settings_save(self, data)
new_flag = self._settings.get_boolean(["active"])
if new_flag != old_flag:
if new_flag:
self._logger.warning("Warning! Activating LDAP Plugin")
settings().set(["accessControl", "userManager"], 'octoprint_authldap.LDAPUserManager')
settings().save()
else:
if settings().get(["accessControl", "userManager"]) == 'octoprint_authldap.LDAPUserManager':
self._logger.warning("Deactivating LDAP Plugin")
settings().remove(["accessControl", "userManager"])
settings().save()

# TemplatePlugin

def get_template_configs(self):
return [
dict(type="settings", template="settings.jinja2")
dict(type="settings", custom_bindings=False)
]


__plugin_name__ = "Auth LDAP"


def __plugin_load__():
global __plugin_implementation__
__plugin_implementation__ = LDAPUserManager()

global __plugin_hooks__
__plugin_hooks__ = {
"octoprint.users.factory": __plugin_implementation__.ldap_user_factory,
"octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information,
"octoprint.users.factory":
__plugin_implementation__.ldap_user_factory,
"octoprint.plugin.softwareupdate.check_config":
__plugin_implementation__.get_update_information,
}


#@TODO Command clean LDAP users deleted
# @TODO Command clean LDAP users deleted
84 changes: 84 additions & 0 deletions octoprint_authldap/templates/authldap_settings.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<form class="form-horizontal">
<h3>LDAP configuration</h3>
<div class="control-group">
<h4>General</h4>
<label class="control-label">{{ _('LDAP URI') }}</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: settings.plugins.authldap.ldap_uri"
placeholder="ldaps://ldap.server.com">
</div>

<label class="control-label">{{ _('Search base pattern') }}</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: settings.plugins.authldap.ldap_search_base"
placeholder="CN=Users,DC=example,DC=com">
</div>

<label for="plugin_ldap_groups" class="control-label">{{ _('Groups (comma-separated if multiple)') }}</label>
<div class="controls">
<input id="plugin_ldap_groups" type="text" class="input-block-level" data-bind="value: settings.accessControl.groups"/>
</div>

<label class="control-label">{{ _('LDAP Filter') }}</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: settings.plugins.authldap.ldap_query"
placeholder="(&(objectclass=user)(memberof=<GROUPNAME>)(samaccountname={uid}))">
</div>

<label class="control-label">{{ _('Default Roles:') }}</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: settings.plugins.authldap.roles"
placeholder="Comma separated list of roles: user,admin">
</div>
<div class="controls">
<label class="checkbox">
<input type="checkbox" data-bind="checked: settings.plugins.authldap.auto_activate">Automatically activate users?
</label>
</div>
</div>
<div class="control-group">
<h4>Authentication</h4>
<label class="control-label">{{ _('Binding User DN') }}</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: settings.plugins.authldap.ldap_bind_user"
placeholder="User DN">
</div>
<label class="control-label">{{ _('Password') }}</label>
<div class="controls">
<input type="password" class="input-block-level" data-bind="value: settings.plugins.authldap.ldap_bind_password">
</div>
</div>
<div class="control-group">
<h4>TLS</h4>
<label class="control-label">{{ _('Method:') }}</label>
<div class="controls">
<select data-bind="value: settings.plugins.authldap.ldap_method">
<option value="BASIC">Basic</option>
<option value="SECURE">TLS</option>
</select>
</div>
<label class="control-label">{{ _('TLS certification check') }}</label>
<div class="controls">
<select data-bind="value: settings.plugins.authldap.ldap_tls_reqcert">
<option value="HARD">Hard</option>
<option value="DEMAND">Demand</option>
<option value="TRY">Try</option>
<option value="ALLOW">Allow</option>
<option value="NEVER">Never</option>
</select>
</div>
</div>
<div class="control-group">
<h4>Activation</h4>
<p>{% trans %}
After activation a restart is needed.<br>
In case there is an issue while initialization, this plugin will disable itself and kill the server.
You can safely restart afterwards.
{% endtrans %}</p>
<div class="controls">
<label class="checkbox">
<input type="checkbox" data-bind="checked: settings.plugins.authldap.active">Activate LDAP Authentication
</label>
</div>
</div>
</form>
Loading