Revert "authorization: add LDAP support"
This reverts commit a86cbc77f6
.
This commit is contained in:
parent
d0cdd76bed
commit
5081321a32
|
@ -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]`
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue