From: Michael Tremer Date: Fri, 9 May 2025 13:10:51 +0000 (+0000) Subject: Implement some authentication X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7ecc50454c77615a91a3ab7d2ee11a667b027ac5;p=people%2Fms%2Fwestferry.git Implement some authentication Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 70b589f..a41768c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -102,6 +102,7 @@ westferry_backend_PYTHON = \ src/westferry/backend/__init__.py \ src/westferry/backend/__version__.py \ src/westferry/backend/base.py \ + src/westferry/backend/errors.py \ src/westferry/backend/graphs.py \ src/westferry/backend/system.py \ src/westferry/backend/users.py @@ -111,6 +112,7 @@ westferry_backenddir = $(pythondir)/westferry/backend westferry_handlers_PYTHON = \ src/westferry/handlers/__init__.py \ src/westferry/handlers/analytics.py \ + src/westferry/handlers/auth.py \ src/westferry/handlers/base.py \ src/westferry/handlers/demo.py \ src/westferry/handlers/index.py \ @@ -139,6 +141,11 @@ dist_templates_DATA = \ src/templates/default.html \ src/templates/graphs.html +templates_authdir = $(templatesdir)/auth + +dist_templates_auth_DATA = \ + src/templates/auth/login.html + templates_demodir = $(templatesdir)/demo dist_templates_demo_DATA = \ diff --git a/src/templates/auth/login.html b/src/templates/auth/login.html new file mode 100644 index 0000000..a219174 --- /dev/null +++ b/src/templates/auth/login.html @@ -0,0 +1,31 @@ +{% extends "../base.html" %} + +{% block body %} +
+
+
+
+
{{ _("Login") }}
+ +
+ {% raw xsrf_form_html() %} + + {% if next %} + + {% end %} + + + + + + +
+
+
+
+
+{% end block %} diff --git a/src/westferry/application.py b/src/westferry/application.py index a402848..0898940 100644 --- a/src/westferry/application.py +++ b/src/westferry/application.py @@ -41,6 +41,9 @@ class WebApplication(tornado.web.Application): # Use Cross-Site-Request-Forgery protection "xsrf_cookies" : True, + + # Send users to /login to authenticate + "login_url" : "/login", } settings.update(kwargs) diff --git a/src/westferry/backend/errors.py b/src/westferry/backend/errors.py new file mode 100644 index 0000000..0b68729 --- /dev/null +++ b/src/westferry/backend/errors.py @@ -0,0 +1,26 @@ +#!/usr/bin/python3 +############################################################################### +# # +# Westferry - The IPFire web user interface # +# Copyright (C) 2022 IPFire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +class UserNotFoundError(Exception): + """ + Raised when a user object could not be found + """ + pass diff --git a/src/westferry/backend/users.py b/src/westferry/backend/users.py index 81ba5cf..4f2c4d5 100644 --- a/src/westferry/backend/users.py +++ b/src/westferry/backend/users.py @@ -19,10 +19,15 @@ # # ############################################################################### +import pam import functools import pydbus +import logging +log = logging.getLogger(__name__) + from . import base +from .errors import UserNotFoundError class UsersBackend(base.BaseBackend): """ @@ -38,10 +43,27 @@ class UsersBackend(base.BaseBackend): "/org/freedesktop/sssd/infopipe/Users", ) + def authenticate(self, name, password): + """ + This function will try to find the user and validate the password. + + On success, a new session will be returned. + """ + user = self.get_by_name(name) + if not user: + return + + # Check password + if not user.authenticate(password): + return + + # All okay. Create a new session + return True # XXX + def get_by_name(self, name): try: return User(self.backend, name) - except NotFoundError: + except UserNotFoundError: return @@ -51,9 +73,11 @@ class User(base.BaseBackend): self.name = name # Search for the user's path in dbus - self.path = self.backend.users.sssd.FindByName(name) - if not self.path: - raise NotFoundError("Could not find user %s" % name) + try: + self.path = self.backend.users.sssd.FindByName(name) + # XXX this should be GDBus.Error:sbus.Error.NotFound + except Exception as e: + raise UserNotFoundError("Could not find user %s" % name) from e def __repr__(self): return "<%s uid=%s>" % (self.__class__.__name__, self.uid) @@ -68,6 +92,17 @@ class User(base.BaseBackend): """ return self.backend.system.bus.get("org.freedesktop.sssd.infopipe", self.path) + def authenticate(self, password): + log.debug("Trying to authenticate user %s" % self.name) + + # Perform the authentication + if pam.authenticate(self.name, password): + log.debug("Successfully authenticated user %s" % self.name) + return True + + log.error("Could not authenticate user %s" % self.name) + return False + @property def gecos(self): """ diff --git a/src/westferry/handlers/__init__.py b/src/westferry/handlers/__init__.py index 5a84838..81706ba 100644 --- a/src/westferry/handlers/__init__.py +++ b/src/westferry/handlers/__init__.py @@ -21,6 +21,7 @@ from . import base from . import analytics +from . import auth from . import demo from . import index from . import system diff --git a/src/westferry/handlers/auth.py b/src/westferry/handlers/auth.py new file mode 100644 index 0000000..bbeee86 --- /dev/null +++ b/src/westferry/handlers/auth.py @@ -0,0 +1,64 @@ +#!/usr/bin/python3 +############################################################################### +# # +# Westferry - The IPFire web user interface # +# Copyright (C) 2022 IPFire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +import tornado.web + +from . import base + +class LoginHandler(base.BaseHandler): + url = r"/login" + + def get(self, next=None, failed=False): + # Tell the user there has been an error + if failed: + self.set_status(401) + + self.render("auth/login.html", next=next, failed=failed) + + def post(self): + username = self.get_argument("username") + password = self.get_argument("password") + + # Where does the user want to be redirected to? + next = self.get_argument("next", None) + + # Try authenticating the user + session = self.backend.users.authenticate(username, password) + + # Credentials have no been valid, sending the user back + if not session: + # Render the login page again + return self.get(next=next, failed=True) + + # Set the session cookie + # XXX + + # Redirect the user back to where they want to go + self.redirect(next or "/") + + +class LogoutHandler(base.BaseHandler): + url = r"/logout" + + @tornado.web.authenticated + def get(self): + # Redirect back to the front page + self.redirect("/") diff --git a/src/westferry/handlers/base.py b/src/westferry/handlers/base.py index abbe1a7..59c0e58 100644 --- a/src/westferry/handlers/base.py +++ b/src/westferry/handlers/base.py @@ -66,7 +66,8 @@ class BaseHandler(tornado.web.RequestHandler, metaclass=HandlerRegistration): This function returns the currently logged-in user. """ # XXX This is just a demo - return self.backend.users.get_by_name("ms") + #return self.backend.users.get_by_name("ms") + pass @property def topmenu(self): diff --git a/src/westferry/handlers/index.py b/src/westferry/handlers/index.py index 6198568..76f1c0b 100644 --- a/src/westferry/handlers/index.py +++ b/src/westferry/handlers/index.py @@ -19,10 +19,13 @@ # # ############################################################################### +import tornado.web + from . import base class IndexHandler(base.BaseHandler): url = r"/" + @tornado.web.authenticated def get(self): self.render("base.html")