Skip to content

Commit

Permalink
mgr: fix pyO3 import issues
Browse files Browse the repository at this point in the history
The latest versions of pyO3 (a Python binding for Rust) explicitly added
a check to detect multiple imports. In subinterpreter environments, this
leads to an ImportError: "PyO3 modules may only be initialized once per
interpreter process" (it has been recenlty replaced with a more
specific: "PyO3 modules may only be initialized once per interpreter
process".

This is only a workaround while the root cause is fixed (see
PyO3/pyo3#4162).

Fixes: https://tracker.ceph.com/issues/64213
Signed-off-by: Ernesto Puerta <epuertat@redhat.com>
  • Loading branch information
epuertat committed Jun 7, 2024
1 parent e082a4d commit 9f2f08f
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 30 deletions.
46 changes: 18 additions & 28 deletions src/pybind/mgr/dashboard/services/access_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@
# pylint: disable=too-many-arguments,too-many-return-statements
# pylint: disable=too-many-branches, too-many-locals, too-many-statements

import base64
import errno
import hashlib
import hmac
import json
import logging
import os
import re
import threading
import time
from datetime import datetime, timedelta
from string import ascii_lowercase, ascii_uppercase, digits, punctuation
from typing import List, Optional, Sequence

import bcrypt
from mgr_module import CLICheckNonemptyFileInput, CLIReadCommand, CLIWriteCommand
from mgr_util import password_hash

from .. import mgr
from ..exceptions import PasswordPolicyException, PermissionNotValid, \
Expand All @@ -26,7 +28,7 @@

logger = logging.getLogger('access_control')
DEFAULT_FILE_DESC = 'password/secret'

SCRYPT_SALT_LEN = 29

_P = Permission # short alias

Expand Down Expand Up @@ -327,8 +329,17 @@ def enabled(self, value):
self._enabled = value
self.refresh_last_update()

@staticmethod
def calculate_password_hash(password: str, input_salt: Optional[str] = None) -> str:
if input_salt is None:
salt = os.urandom(SCRYPT_SALT_LEN)
else:
salt = base64.b64decode(salt)[:SCRYPT_SALT_LEN]
hash = hashlib.scrypt(password.encode('utf8'), salt=salt, n=2**14, r=8, p=1)
return base64.b64encode(salt + hash).decode('utf8')

def set_password(self, password):
self.set_password_hash(password_hash(password))
self.set_password_hash(self.calculate_password_hash(password))

def set_password_hash(self, hashed_password):
self.invalid_auth_attempt = 0
Expand All @@ -345,8 +356,8 @@ def compare_password(self, password):
:return: `True` if the passwords are equal, otherwise `False`.
:rtype: bool
"""
pass_hash = password_hash(password, salt_password=self.password)
return pass_hash == self.password
pass_hash = self.calculate_password_hash(password, salt_password=self.password)
return hmac.compare_digest(pass_hash, self.password)

def is_pwd_expired(self):
if self.pwd_expiration_date:
Expand Down Expand Up @@ -475,7 +486,7 @@ def create_user(self, username, password, name, email, enabled=True,
if pwd_expiration_date and \
(pwd_expiration_date < int(time.mktime(datetime.utcnow().timetuple()))):
raise PwdExpirationDateNotValid()
user = User(username, password_hash(password), name, email, enabled=enabled,
user = User(username, User.calculate_password_hash(password), name, email, enabled=enabled,
pwd_expiration_date=pwd_expiration_date,
pwd_update_required=pwd_update_required)
self.users[username] = user
Expand Down Expand Up @@ -880,27 +891,6 @@ def ac_user_set_password(_, username: str, inbuf: str,
return -errno.ENOENT, '', str(ex)


@CLIWriteCommand('dashboard ac-user-set-password-hash')
@CLICheckNonemptyFileInput(desc=DEFAULT_FILE_DESC)
def ac_user_set_password_hash(_, username: str, inbuf: str):
'''
Set user password bcrypt hash from -i <file>
'''
hashed_password = inbuf
try:
# make sure the hashed_password is actually a bcrypt hash
bcrypt.checkpw(b'', hashed_password.encode('utf-8'))
user = mgr.ACCESS_CTRL_DB.get_user(username)
user.set_password_hash(hashed_password)

mgr.ACCESS_CTRL_DB.save()
return 0, json.dumps(user.to_dict()), ''
except ValueError:
return -errno.EINVAL, '', 'Invalid password hash'
except UserDoesNotExist as ex:
return -errno.ENOENT, '', str(ex)


@CLIWriteCommand('dashboard ac-user-set-info')
def ac_user_set_info(_, username: str, name: str, email: str):
'''
Expand Down
5 changes: 3 additions & 2 deletions src/pybind/mgr/mgr_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
if 'UNITTEST' in os.environ:
import tests

import bcrypt
import cephfs
import contextlib
import datetime
Expand Down Expand Up @@ -516,7 +515,7 @@ def create_self_signed_cert(organisation: str = 'Ceph',
:param organisation: String representing the Organisation(O) RDN (default='Ceph')
:param common_name: String representing the Common Name(CN) RDN (default='mgr')
:param dname: Optional dictionary containing RDNs to use for crt/key generation
:param dname: Optional dictionary containing RDNs to use for crt/key generation
:return: ssl crt and key in utf-8 format
Expand Down Expand Up @@ -870,6 +869,8 @@ def wrapper(*args: Any, **kwargs: Any) -> T:


def password_hash(password: Optional[str], salt_password: Optional[str] = None) -> Optional[str]:
import bcrypt

if not password:
return None
if not salt_password:
Expand Down

0 comments on commit 9f2f08f

Please sign in to comment.