diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 5adbcf2e..a574776b 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -336,6 +336,7 @@ class Server(object): loginManager = LoginManager() loginManager.session_protection = "strong" loginManager.user_callback = load_user + loginManager.anonymous_user = users.AnonymousUser # TODO: remove in 1.5.0 if not userManager.enabled: loginManager.anonymous_user = users.DummyUser principals.identity_loaders.appendleft(users.dummy_identity_loader) diff --git a/src/octoprint/users.py b/src/octoprint/users.py index 1803104b..fb3fc83d 100644 --- a/src/octoprint/users.py +++ b/src/octoprint/users.py @@ -5,7 +5,7 @@ __author__ = "Gina Häußge " __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License" -from flask_login import UserMixin +from flask_login import UserMixin, AnonymousUserMixin from flask_principal import Identity from werkzeug.local import LocalProxy import hashlib @@ -413,6 +413,65 @@ class UnknownRole(Exception): ##~~ User object +class MethodReplacedByBooleanProperty(object): + + def __init__(self, name, message, getter): + self._name = name + self._message = message + self._getter = getter + + @property + def _attr(self): + return self._getter() + + def __call__(self): + from warnings import warn + warn(DeprecationWarning(self._message.format(name=self._name)), stacklevel=2) + return self._attr + + def __eq__(self, other): + return self._attr == other + + def __ne__(self, other): + return self._attr != other + + def __bool__(self): + # Python 3 + return self._attr + + def __nonzero__(self): + # Python 2 + return self._attr + + def __hash__(self): + return hash(self._attr) + + def __repr__(self): + return "MethodReplacedByProperty({}, {}, {})".format(self._name, self._message, self._getter) + + def __str__(self): + return str(self._attr) + + +# TODO: Remove compatibility layer in OctoPrint 1.5.0 +class FlaskLoginMethodReplacedByBooleanProperty(MethodReplacedByBooleanProperty): + + def __init__(self, name, getter): + message = "{name} is now a property in Flask-Login versions >= 0.3.0, which OctoPrint now uses. " + \ + "Use {name} instead of {name}(). This compatibility layer will be removed in OctoPrint 1.5.0." + MethodReplacedByBooleanProperty.__init__(self, name, message, getter) + + +# TODO: Remove compatibility layer in OctoPrint 1.5.0 +class OctoPrintUserMethodReplacedByBooleanProperty(MethodReplacedByBooleanProperty): + + def __init__(self, name, getter): + message = "{name} is now a property for consistency reasons with Flask-Login versions >= 0.3.0, which " + \ + "OctoPrint now uses. Use {name} instead of {name}(). This compatibility layer will be removed " + \ + "in OctoPrint 1.5.0." + MethodReplacedByBooleanProperty.__init__(self, name, message, getter) + + class User(UserMixin): def __init__(self, username, passwordHash, active, roles, apikey=None, settings=None): self._username = username @@ -428,9 +487,9 @@ class User(UserMixin): def asDict(self): return { "name": self._username, - "active": self.is_active, - "admin": self.is_admin, - "user": self.is_user, + "active": bool(self.is_active), + "admin": bool(self.is_admin), + "user": bool(self.is_user), "apikey": self._apikey, "settings": self._settings } @@ -444,17 +503,25 @@ class User(UserMixin): def get_name(self): return self._username + @property + def is_anonymous(self): + return FlaskLoginMethodReplacedByBooleanProperty("is_anonymous", lambda: False) + + @property + def is_authenticated(self): + return FlaskLoginMethodReplacedByBooleanProperty("is_authenticated", lambda: True) + @property def is_active(self): - return self._active + return FlaskLoginMethodReplacedByBooleanProperty("is_active", lambda: self._active) @property def is_user(self): - return "user" in self._roles + return OctoPrintUserMethodReplacedByBooleanProperty("is_user", lambda: "user" in self._roles) @property def is_admin(self): - return "admin" in self._roles + return OctoPrintUserMethodReplacedByBooleanProperty("is_admin", lambda: "admin" in self._roles) def get_all_settings(self): return self._settings @@ -505,6 +572,22 @@ class User(UserMixin): def __repr__(self): return "User(id=%s,name=%s,active=%r,user=%r,admin=%r)" % (self.get_id(), self.get_name(), self.is_active, self.is_user, self.is_admin) + +class AnonymousUser(AnonymousUserMixin): + + @property + def is_anonymous(self): + return FlaskLoginMethodReplacedByBooleanProperty("is_anonymous", lambda: True) + + @property + def is_authenticated(self): + return FlaskLoginMethodReplacedByBooleanProperty("is_authenticated", lambda: False) + + @property + def is_active(self): + return FlaskLoginMethodReplacedByBooleanProperty("is_active", lambda: False) + + class SessionUser(User): def __init__(self, user): self._user = user