Exemplo n.º 1
0
def user(ctx):
    """
	User management.

	Note that this currently only supports managing user accounts stored in the configured user manager, not any
	user managers added through plugins and the "octoprint.users.factory" hook.
	"""
    try:
        logging.basicConfig(
            level=logging.DEBUG
            if get_ctx_obj_option(ctx, "verbosity", 0) > 0 else logging.WARN)
        settings = init_settings(get_ctx_obj_option(ctx, "basedir", None),
                                 get_ctx_obj_option(ctx, "configfile", None))

        name = settings.get(["accessControl", "userManager"])
        try:
            clazz = get_class(name)
            manager = clazz(settings=settings)
        except Exception:
            click.echo(
                "Could not instantiate user manager {}, falling back to FilebasedUserManager!"
                .format(name),
                err=True)
            manager = FilebasedUserManager(settings=settings)
        finally:
            manager.enabled = settings.getBoolean(["accessControl", "enabled"])

        ctx.obj.user_manager = manager

    except Exception:
        click.echo("Could not instantiate user manager", err=True)
        return
Exemplo n.º 2
0
class LDAPUserManager(FilebasedUserManager, octoprint.plugin.SettingsPlugin,
                      octoprint.plugin.TemplatePlugin):

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

    _localUserManager = FilebasedUserManager()

    def __init__(self):
        if settings().get(["accessControl", "userManager"
                           ]) == 'octoprint_authldap.LDAPUserManager':
            if settings().get(["plugins", "authldap", "ldap_uri"]) is not None\
              and settings().get(["plugins", "authldap", "ldap_bind_user"]) is not None \
              and settings().get(["plugins", "authldap", "ldap_bind_password"]) is not None \
              and settings().get(["plugins", "authldap", "ldap_search_base"]) is not None \
              and settings().get(["plugins", "authldap", "ldap_query"]) is not None:
                connection = ldap.initialize(settings().get(
                    ["plugins", "authldap", "ldap_uri"]))
                connection.set_option(ldap.OPT_REFERRALS, 0)
                if settings().get(["plugins", "authldap",
                                   "ldap_method"]) == 'SECURE':
                    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
                    # elif ldap_verifypeer == 'HARD':
                    #   verifypeer = ldap.OPT_X_TLS_HARD
                    connection.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,
                                          verifypeer)
                    try:
                        connection.start_tls_s()
                    except:
                        pass
                try:
                    connection.simple_bind_s(
                        settings().get(
                            ["plugins", "authldap", "ldap_bind_user"]),
                        settings().get(
                            ["plugins", "authldap", "ldap_bind_password"]))
                    connection.unbind_s()

                    FilebasedUserManager.__init__(self)
                    return
                except:
                    pass
            #settings().remove(["accessControl", "userManager"])
            #settings().remove(["plugins", "authldap", "active"])
            #settings().save()
            #os.system('kill $PPID')

    def findUser(self, userid=None, apikey=None, session=None):
        user = UserManager.findUser(self, userid=userid, session=session)

        if user is not None:
            self._logger.debug("User already logged in.")
            return user

        elif apikey is not None:
            self._logger.debug("User logs in via API Key.")
            return self._localUserManager.findUser(apikey=apikey,
                                                   session=session)

        elif userid is not None:
            self._logger.debug("User not yet logged in: %s" % userid)
            local_user = self._localUserManager.findUser(userid=userid,
                                                         session=session)
            if local_user is not None:
                self._logger.debug("User found locally")
                return local_user
            elif self.findLDAPUser(userid):
                self._logger.debug("User found in LDAP")
                password = ''.join(
                    [random.choice(string.lowercase) for i in range(10)])
                if settings().get(["plugins", "authldap",
                                   "roles"]) is not None:
                    roles = [
                        x.strip() for x in settings().get(
                            ["plugins", "authldap", "roles"]).split(',')
                    ]
                self._localUserManager.addUser(userid,
                                               password,
                                               active=settings().getBoolean([
                                                   "plugins", "authldap",
                                                   "auto_activate"
                                               ]),
                                               roles=roles)
                return self._localUserManager.findUser(userid=userid,
                                                       session=session)
            else:
                return None
        else:
            return None

    def findLDAPUser(self, userid):
        userid = self.escapeLDAP(userid)
        self._logger.debug("Searching User in ldap: %s" % userid)

        connection = self.getLDAPClient()
        try:
            self._logger.debug(
                "Binding LDAP with User: %s" %
                settings().get(["plugins", "authldap", "ldap_bind_user"]))
            connection.simple_bind_s(
                settings().get(["plugins", "authldap", "ldap_bind_user"]),
                settings().get(["plugins", "authldap", "ldap_bind_password"]))
            query = settings().get(["plugins", "authldap",
                                    "ldap_query"]).format(uid=userid)
            self._logger.debug(
                "Searching for \"uid=%s\" under \"%s\" " %
                (query, settings().get(
                    ["plugins", "authldap", "ldap_search_base"])))
            result = connection.search_s(
                settings().get(["plugins", "authldap", "ldap_search_base"]),
                ldap.SCOPE_SUBTREE, query)
            connection.unbind_s()
            self._logger.debug("Search finished.")
            if result is None or len(result) == 0:
                self._logger.debug("No User Found in LDAP")
                return None
            # Get the DN of first user found
            dn, data = result[0]
            self._logger.debug("User Found %s" % dn)
            return dn
        except ldap.SERVER_DOWN:
            self._logger.debug("LDAP Server unreachable!")
        return None

    def checkPassword(self, username, password):
        self._logger.debug("Checking Password")
        if self.findLDAPUser(username) is not None:
            self._logger.debug("User is a LDAP User")
            try:
                connection = self.getLDAPClient()
                username = self.escapeLDAP(username)
                dn = self.findLDAPUser(username)
                connection.simple_bind_s(dn, password)
                connection.unbind_s()
                return True

            except ldap.INVALID_CREDENTIALS:
                self._logger.error("Username or password is incorrect.")
        else:
            self._logger.debug("User is a local user")
            return self._localUserManager.checkPassword(username, password)

    def changeUserPassword(self, username, password):
        # Changing password of LDAP users is not allowed
        if self.findLDAPUser(username) is None:
            return self._localUserManager.changeUserPassword(
                username, password)
        else:
            self._logger.error("User is not allowed to change the Password.")

    def getLDAPClient(self):
        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:
            settings().remove(["accessControl", "userManager"])
            self._logger.debug(
                "UserManager: %s" %
                settings().get(["accessControl", "userManager"]))
            raise Exception("LDAP conf error, server is missing")

        connection = ldap.initialize(ldap_server)
        connection.set_option(ldap.OPT_REFERRALS, 0)
        self._logger.debug("LDAP initialized")

        if settings().get(["plugins", "authldap", "ldap_method"]) == 'SECURE':
            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
            # elif ldap_verifypeer == 'HARD':
            #   verifypeer = ldap.OPT_X_TLS_HARD
            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
        self._logger.error("Finished creating connection")
        return connection

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

    # Softwareupdate hook

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

            # version check: github repository
            type="github_release",
            user="******",
            repo="OctoPrint-LDAP",
            current=self._plugin_version,

            # update method: pip
            pip=("https://github.com"
                 "/bveina/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(active=False,
                    ldap_uri=None,
                    ldap_search_base=None,
                    ldap_query=None,
                    ldap_method=None,
                    auto_activate=False,
                    roles=None,
                    ldap_tls_reqcert=None,
                    ldap_bind_user=None,
                    ldap_bind_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", custom_bindings=False)]