New hook octoprint.accesscontrol.keyvalidator

Allows plugins to provide their own API keys which then can be
validated/translated into a User instance via this hook.
This commit is contained in:
Gina Häußge 2017-10-19 13:15:18 +02:00
parent a9819cc2f9
commit e7b455f880
2 changed files with 64 additions and 11 deletions

View file

@ -226,6 +226,37 @@ octoprint.accesscontrol.appkey
:return: A list of 3-tuples as described above
:rtype: list
.. _sec-plugins-hook-accesscontrol-keyvalidator:
octoprint.accesscontrol.keyvalidator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. py:function:: acl_keyvalidator_hook(apikey, *args, **kwargs)
Via this hook plugins may validate their own customized API keys to be used to access OctoPrint's API.
``apikey`` will be the API key as read from the request headers.
Hook handlers are expected to return a :class:`~octoprint.users.User` instance here that will then be considered that
user making the request. By returning ``None`` or nothing at all, hook handlers signal that they do not handle the
provided key.
**Example:**
Allows using a user's id as their API key (for obvious reasons this is NOT recommended in production environments
and merely provided for educational purposes):
.. onlineinclude:: https://raw.githubusercontent.com/OctoPrint/Plugin-Examples/master/custom_keyvalidator.py
:linenos:
:tab-width: 4
:caption: `custom_keyvalidator.py <https://github.com/OctoPrint/Plugin-Examples/blob/master/custom_keyvalidator.py>`_
.. versionadded:: 1.3.6
:param str apikey: The API key to validate
:return: The user in whose name the request will be processed further
:rtype: :class:`~octoprint.users.User`
.. _sec-plugins-hook-cli-commands:
octoprint.cli.commands

View file

@ -11,8 +11,10 @@ import octoprint.server
from octoprint.users import ApiUser
from octoprint.util import deprecated
from octoprint.plugin import plugin_manager
import flask as _flask
import logging
from . import flask
from . import sockjs
@ -55,14 +57,22 @@ def loginFromApiKeyRequestHandler():
"""
apikey = get_api_key(_flask.request)
if apikey and apikey != octoprint.server.UI_API_KEY and not octoprint.server.appSessionManager.validate(apikey):
user = get_user_for_apikey(apikey)
if user is not None and _flask.ext.login.login_user(user, remember=False):
_flask.ext.principal.identity_changed.send(_flask.current_app._get_current_object(),
identity=_flask.ext.principal.Identity(user.get_id()))
else:
return _flask.make_response("Invalid API key", 401)
if not apikey:
return
if apikey == octoprint.server.UI_API_KEY:
return
if octoprint.server.appSessionManager.validate(apikey):
return
user = get_user_for_apikey(apikey)
if user is not None and _flask.ext.login.login_user(user, remember=False):
_flask.ext.principal.identity_changed.send(_flask.current_app._get_current_object(),
identity=_flask.ext.principal.Identity(user.get_id()))
else:
return _flask.make_response("Invalid API key", 401)
def corsRequestHandler():
@ -143,9 +153,21 @@ def get_user_for_apikey(apikey):
if apikey == settings().get(["api", "key"]) or octoprint.server.appSessionManager.validate(apikey):
# master key or an app session key was used
return ApiUser()
elif octoprint.server.userManager.enabled:
# user key might have been used
return octoprint.server.userManager.findUser(apikey=apikey)
if octoprint.server.userManager.enabled:
user = octoprint.server.userManager.findUser(apikey=apikey)
if user is not None:
# user key was used
return user
apikey_hooks = plugin_manager().get_hooks("octoprint.accesscontrol.keyvalidator")
for name, hook in apikey_hooks.items():
try:
user = hook(apikey)
if user is not None:
return user
except:
logging.getLogger(__name__).exception("Error running api key validator for plugin {} and key {}".format(name, apikey))
return None