From 5081321a32e53b1b032268743f6bb5030bc565fd Mon Sep 17 00:00:00 2001 From: Eric Callahan Date: Wed, 8 Jun 2022 06:23:19 -0400 Subject: [PATCH] Revert "authorization: add LDAP support" This reverts commit a86cbc77f6182198e1058752a8ee77a516b01b7e. --- docs/configuration.md | 22 ----- docs/web_api.md | 14 +--- moonraker/components/authorization.py | 114 +++----------------------- scripts/moonraker-requirements.txt | 1 - 4 files changed, 14 insertions(+), 137 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 335c26d..7b0cc86 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -285,28 +285,6 @@ force_logins: False # one user has been created, overriding the "trusted_clients" configuration. # If no users have been created then trusted client checks will apply. # The default is False. -default_source: moonraker -# If the default_source is set to "ldap", a user login is required for authorization. -# The default_source is set to "moonraker" by default. - -# Providing a correct configuration for an LDAP session -# is required because moonraker does not verify the provided configuration. -ldap_server: ldap.local -ldap_base_dn: DC=ldap,DC=local -ldap_secure: True -# To use LDAPs(LDAP over SSL/TLS), please set ldap_secure to True. -ldap_type_ad: True -# Set ldap_type_ad to True if you use Microsoft Active Directory. -ldap_bind_dn: {secrets.ldap_credentials.bind_dn} -# The distinguished name for bind authentication. It should look like this -# CN=moonraker,OU=Users,DC=ldap,DC=local. This option accepts -# Jinja2 Templates, see the [secrets] section for details. -ldap_bind_password: {secrets.ldap_credentials.bind_password} -# The password for bind authentication. This option accepts -# Jinja2 Templates, see the [secrets] section for details. -ldap_group_dn: CN=moonraker,OU=Groups,DC=ldap,DC=local -# The ldap_group_dn must be in the memberOf list of the user. -# If this option is not filled, a successful authentication is enough. ``` ### `[octoprint_compat]` diff --git a/docs/web_api.md b/docs/web_api.md index 532781e..9086b97 100644 --- a/docs/web_api.md +++ b/docs/web_api.md @@ -1949,12 +1949,11 @@ GET /access/user ``` JSON-RPC request: Not Available -Returns: An object containing the currently logged in user name, the source and +Returns: An object containing the currently logged in user name and the date on which the user was created (in unix time). ```json { "username": "my_user", - "source": "moonraker", "created_on": 1618876783.8896716 } ``` @@ -1967,21 +1966,19 @@ Content-Type: application/json { "username": "my_user", - "password": "my_password", - "source": "moonraker", + "password": "my_password" } ``` JSON-RPC request: Not Available Returns: An object containing the created user name, an auth token, -a refresh token, the source, and an action summary. Creating a user also effectively +a refresh token, and an action summary. Creating a user also effectively logs the user in. ```json { "username": "my_user", "token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzY3ODMuODkxNjE5LCAiZXhwIjogMTYxODg4MDM4My44OTE2MTksICJ1c2VybmFtZSI6ICJteV91c2VyIiwgInRva2VuX3R5cGUiOiAiYXV0aCJ9.oH0IShTL7mdlVs4kcx3BIs_-1j0Oe-qXezJKjo-9Xgo", "refresh_token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzY3ODMuODkxNzAyNCwgImV4cCI6IDE2MjY2NTI3ODMuODkxNzAyNCwgInVzZXJuYW1lIjogIm15X3VzZXIiLCAidG9rZW5fdHlwZSI6ICJyZWZyZXNoIn0.a6ZeRjk8RQQJDDH0JV-qGY_d_HIgfI3XpsqUlUaFT7c", - "source": "moonraker", "action": "user_created" } ``` @@ -2031,12 +2028,10 @@ Returns: A list of created users on the system "users": [ { "username": "testuser", - "source": "moonraker", "created_on": 1618771331.1685035 }, { "username": "testuser2", - "source": "ldap", "created_on": 1620943153.0191233 } ] @@ -2081,12 +2076,11 @@ Content-Type: application/json JSON-RPC request: Not Available -Returns: The username, new auth token, the source and action summary. +Returns: The username, new auth token, and action summary. ```json { "username": "my_user", "token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzgyNDMuNTE2Nzc5MiwgImV4cCI6IDE2MTg4ODE4NDMuNTE2Nzc5MiwgInVzZXJuYW1lIjogInRlc3R1c2VyIiwgInRva2VuX3R5cGUiOiAiYXV0aCJ9.Ia_X_pf20RR4RAEXcxalZIOzOBOs2OwearWHfRnTSGU", - "source": "moonraker", "action": "user_jwt_refresh" } ``` diff --git a/moonraker/components/authorization.py b/moonraker/components/authorization.py index 53866a6..7fa58c2 100644 --- a/moonraker/components/authorization.py +++ b/moonraker/components/authorization.py @@ -32,16 +32,6 @@ from typing import ( Dict, List, ) -from bonsai import ( - LDAPClient, - LDAPSearchScope -) -from bonsai.errors import ( - ConnectionError, - AuthenticationError, - TimeoutError, -) - if TYPE_CHECKING: from confighelper import ConfigHelper from websockets import WebRequest @@ -83,24 +73,6 @@ class Authorization: self.server = config.get_server() self.login_timeout = config.getint('login_timeout', 90) self.force_logins = config.getboolean('force_logins', False) - self.ldap_server = config.get('ldap_server', None) - self.ldap_base_dn = config.get('ldap_base_dn', None) - self.ldap_group_dn = config.get('ldap_group_dn', None) - self.ldap_type_ad = config.getboolean('ldap_type_ad', False) - self.ldap_secure = config.getboolean('ldap_secure', False) - - ldap_bind_dn_template = config.gettemplate('ldap_bind_dn', None) - self.ldap_bind_dn: Optional[str] = None - if ldap_bind_dn_template is not None: - self.ldap_bind_dn = ldap_bind_dn_template.render() - - ldap_bind_password_template = config.gettemplate('ldap_bind_password', - None) - self.ldap_bind_password: Optional[str] = None - if ldap_bind_password_template is not None: - self.ldap_bind_password = ldap_bind_password_template.render() - - self.default_source = config.get('default_source', "moonraker") database: DBComp = self.server.lookup_component('database') database.register_local_namespace('authorized_users', forbidden=True) self.user_db = database.wrap_namespace('authorized_users') @@ -280,7 +252,7 @@ class Authorization: return self.get_oneshot_token(ip, user_info) async def _handle_login(self, web_request: WebRequest) -> Dict[str, Any]: - return await self._login_jwt_user(web_request) + return self._login_jwt_user(web_request) async def _handle_logout(self, web_request: WebRequest) -> Dict[str, str]: user_info = web_request.get_current_user() @@ -316,8 +288,6 @@ class Authorization: return { 'username': username, 'token': token, - 'source': '%s' % (user_info['source'] if 'source' in user_info - else "moonraker"), 'action': 'user_jwt_refresh' } @@ -330,19 +300,16 @@ class Authorization: if user is None: return { 'username': None, - 'source': None, 'created_on': None, } else: return { 'username': user['username'], - 'source': '%s' % (user['source'] if 'source' in user - else "moonraker"), 'created_on': user.get('created_on') } elif action == "POST": # Create User - return await self._login_jwt_user(web_request, create=True) + return self._login_jwt_user(web_request, create=True) elif action == "DELETE": # Delete User return self._delete_jwt_user(web_request) @@ -357,8 +324,6 @@ class Authorization: continue user_list.append({ 'username': user['username'], - 'source': '%s' % (user['source'] if 'source' in user - else "moonraker"), 'created_on': user['created_on'] }) return { @@ -374,9 +339,6 @@ class Authorization: if user_info is None: raise self.server.error("No Current User") username = user_info['username'] - if user_info['source'] == "ldap": - raise self.server.error( - f"CanĀ“t Reset password for ldap user {username}") if username in RESERVED_USERS: raise self.server.error( f"Invalid Reset Request for user {username}") @@ -394,64 +356,16 @@ class Authorization: 'action': "user_password_reset" } - async def _login_ldap_user(self, username, password) -> bool: - if self.ldap_server is None or self.ldap_base_dn is None \ - or self.ldap_group_dn is None \ - or self.ldap_bind_password is None \ - or self.ldap_bind_dn is None: - raise self.server.error( - "ldap: Configuration is not given", 401 - ) - base_dn = str(self.ldap_base_dn) - client = LDAPClient(self._generate_ldap_url_(str(self.ldap_server))) - client.set_credentials("SIMPLE", self.ldap_bind_dn, - self.ldap_bind_password) - client.set_cert_policy("allow") - bind_success = False - try: - async with client.connect(is_async=True, timeout=10) as conn: - ldap_filter = ("(&(objectClass=Person)(%s=" + '%s))') % \ - ("sAMAccountName" - if self.ldap_type_ad - else "uid", - username) - user = await conn.search( - base_dn, - LDAPSearchScope.SUBTREE, - ldap_filter, - ['memberOf'] - ) - auth_username = str(user[0]["DN"]) - client.set_credentials("SIMPLE", auth_username, password) - bind_success = True - async with client.connect(is_async=True, timeout=10): - if self.ldap_group_dn is None: - return True - if len(user[0]['memberOf']) > 0: - for group in user[0]['memberOf']: - if str(group) == str(self.ldap_group_dn): - return True - except (ConnectionError, AuthenticationError, TimeoutError): - if not bind_success: - raise self.server.error("ldap: bind error", 401) - raise self.server.error("ldap: Invalid Username or Password", - 401) - - async def _login_jwt_user(self, - web_request: WebRequest, - create: bool = False - ) -> Dict[str, Any]: + def _login_jwt_user(self, + web_request: WebRequest, + create: bool = False + ) -> Dict[str, Any]: username: str = web_request.get_str('username') password: str = web_request.get_str('password') - source: str = web_request.get_str('source', self.default_source).lower() user_info: Dict[str, Any] if username in RESERVED_USERS: raise self.server.error( f"Invalid Request for user {username}") - if source == "ldap" and not create: - await self._login_ldap_user(username, password) - if username not in self.users: - create = True if create: if username in self.users: raise self.server.error(f"User {username} already exists") @@ -462,14 +376,11 @@ class Authorization: 'username': username, 'password': hashed_pass, 'salt': salt.hex(), - 'source': source, 'created_on': time.time() } self.users[username] = user_info self._sync_user(username) action = "user_created" - if source == "ldap": - action = "user_logged_in" else: if username not in self.users: raise self.server.error(f"Unregistered User: {username}") @@ -478,8 +389,8 @@ class Authorization: hashed_pass = hashlib.pbkdf2_hmac( 'sha256', password.encode(), salt, HASH_ITER).hex() action = "user_logged_in" - if hashed_pass != user_info['password']: - raise self.server.error("Invalid Password") + if hashed_pass != user_info['password']: + raise self.server.error("Invalid Password") jwt_secret_hex: Optional[str] = user_info.get('jwt_secret', None) if jwt_secret_hex is None: private_key = Signer() @@ -505,8 +416,6 @@ class Authorization: return { 'username': username, 'token': token, - 'source': '%s' % (user_info['source'] if 'source' in user_info - else "moonraker"), 'refresh_token': refresh_token, 'action': action } @@ -622,9 +531,6 @@ class Authorization: 'use': "sig" } - def _generate_ldap_url_(self, url: str) -> str: - return "ldap%s://%s" % ("s" if self.ldap_secure else "", url) - def _public_key_from_jwk(self, jwk: Dict[str, Any]) -> Verifier: if jwk.get('kty') != "OKP": raise self.server.error("Not an Octet Key Pair") @@ -735,8 +641,8 @@ class Authorization: def check_authorized(self, request: HTTPServerRequest ) -> Optional[Dict[str, Any]]: - if request.path in self.permitted_paths \ - or request.method == "OPTIONS": + if request.path in self.permitted_paths or \ + request.method == "OPTIONS": return None # Check JSON Web Token diff --git a/scripts/moonraker-requirements.txt b/scripts/moonraker-requirements.txt index f0897d3..ba77853 100644 --- a/scripts/moonraker-requirements.txt +++ b/scripts/moonraker-requirements.txt @@ -15,4 +15,3 @@ preprocess-cancellation==0.2.0 jinja2==3.0.3 dbus-next==0.2.3 apprise==0.9.7 -bonsai==1.4.0