From de5a2e1c65c489c01553c6a8973a9bda9bf65e8f Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Sat, 31 Oct 2020 00:02:02 +0100 Subject: [PATCH] Added registration feature --- flaschengeist.example.toml | 9 ++++++ flaschengeist/controller/userController.py | 14 +++++++++ flaschengeist/plugins/__init__.py | 20 +++++++++---- flaschengeist/plugins/auth_plain/__init__.py | 9 ++++++ flaschengeist/plugins/users/__init__.py | 31 ++++++++++++++++---- 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/flaschengeist.example.toml b/flaschengeist.example.toml index a804467..a07cd66 100644 --- a/flaschengeist.example.toml +++ b/flaschengeist.example.toml @@ -32,6 +32,15 @@ enabled = true ## ADMIN_DN: ## ADMIN_SECRET: +#[users] +# allways enabled +# +## allowed values: false, "managed", "public" +## false: Disable registration +## "managed": only users with matching permission are allowed to register new users +## "public": Also unautheticated users can register an account +# registration = False + ############################ # Configuration of plugins # ############################ diff --git a/flaschengeist/controller/userController.py b/flaschengeist/controller/userController.py index 4ee2340..d5dd7f8 100644 --- a/flaschengeist/controller/userController.py +++ b/flaschengeist/controller/userController.py @@ -68,3 +68,17 @@ def delete(user): current_app.config["FG_AUTH_BACKEND"].delete_user(user) db.session.delete(user) db.session.commit() + + +def register(data): + for required in ["firstname", "lastname", "mail"]: + if required not in data: + raise BadRequest("Missing required parameters") + allowed_keys = User().serialize().keys() + user = User(**{key: value for key, value in data.items() if key in allowed_keys}) + + current_app.config["FG_AUTH_BACKEND"].create_user(user, data["password"]) + + db.session.add(user) + db.session.commit() + return user diff --git a/flaschengeist/plugins/__init__.py b/flaschengeist/plugins/__init__.py index 873bf0c..46aa1a1 100644 --- a/flaschengeist/plugins/__init__.py +++ b/flaschengeist/plugins/__init__.py @@ -1,4 +1,5 @@ import pkg_resources +from werkzeug.exceptions import MethodNotAllowed from flaschengeist.hook import HookCall @@ -31,13 +32,12 @@ class AuthPlugin(Plugin): Returns: Must return False if not found or invalid credentials, True if success """ - raise NotImplementedError + raise NotImplemented def update_user(self, user): """If backend is using external data, then update this user instance with external data - ) - Args: - user: User object + Args: + user: User object """ pass @@ -55,6 +55,16 @@ class AuthPlugin(Plugin): """ raise NotImplemented + def create_user(self, user, password): + """If backend is using (writeable) external data, then create a new user on the external database. + + Args: + user: User object + password: string + + """ + raise MethodNotAllowed + def delete_user(self, user): """If backend is using (writeable) external data, then delete the user from external database. @@ -62,4 +72,4 @@ class AuthPlugin(Plugin): user: User object """ - pass + raise MethodNotAllowed diff --git a/flaschengeist/plugins/auth_plain/__init__.py b/flaschengeist/plugins/auth_plain/__init__.py index cdccb5b..49575e5 100644 --- a/flaschengeist/plugins/auth_plain/__init__.py +++ b/flaschengeist/plugins/auth_plain/__init__.py @@ -24,6 +24,15 @@ class AuthPlain(AuthPlugin): if new_password: user.set_attribute("password", AuthPlain._hash_password(new_password)) + def create_user(self, user, password): + if not user.userid: + raise BadRequest("userid is missing for new user") + hashed = AuthPlain._hash_password(password) + user.set_attribute("password", hashed) + + def delete_user(self, user): + pass + @staticmethod def _hash_password(password): salt = hashlib.sha256(os.urandom(60)).hexdigest().encode("ascii") diff --git a/flaschengeist/plugins/users/__init__.py b/flaschengeist/plugins/users/__init__.py index 648ba1d..d79cc5c 100644 --- a/flaschengeist/plugins/users/__init__.py +++ b/flaschengeist/plugins/users/__init__.py @@ -2,19 +2,20 @@ Provides routes used to manage users """ - +from flaschengeist.config import config from flask import Blueprint, request, jsonify -from werkzeug.exceptions import NotFound, BadRequest, Forbidden +from werkzeug.exceptions import BadRequest, Forbidden, MethodNotAllowed from flaschengeist import logger from flaschengeist.plugins import Plugin -from flaschengeist.decorator import login_required +from flaschengeist.decorator import login_required, extract_session from flaschengeist.controller import userController users_bp = Blueprint("users", __name__) _permission_edit = "users_edit_other" _permission_set_roles = "users_set_roles" _permission_delete = "users_delete_other" +_permission_register = "users_register" class UsersPlugin(Plugin): @@ -23,9 +24,29 @@ class UsersPlugin(Plugin): @users_bp.route("/users", methods=["POST"]) -def __registration(self): +def register(): + """Register a new user + + Route: ``/users`` | Method: ``POST`` + + POST-data: Same as `flaschengeist.models.user.User` + ``password?: string`` + + Returns: + JSON encoded `flaschengeist.models.user.User` or HTTP error + """ + registration = config["users"].get("registration", False) + if not registration or registration not in ["managed", "public"]: + logger.debug("Config for Registration is set to >{}<".format(registration)) + raise MethodNotAllowed + if registration == "managed": + extract_session(_permission_register) + + data = request.get_json() + if not data: + raise BadRequest logger.debug("Register new User...") - return jsonify({"ok": "ok... well not implemented"}) + + return jsonify(userController.register(data)) @users_bp.route("/users", methods=["GET"])