Skip to content

Commit

Permalink
Merge branch 'main' 1.2.6dev into production
Browse files Browse the repository at this point in the history
  • Loading branch information
LoanR committed Apr 30, 2024
2 parents 50a77c7 + 1304260 commit 80d76f6
Show file tree
Hide file tree
Showing 10 changed files with 405 additions and 179 deletions.
307 changes: 154 additions & 153 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "b3desk"
version = "1.2.5"
version = "1.2.6"
description = "Outil de visioconférence pour les agents de l'Education Nationale et de l'Etat en général."
authors = ["Your Name <[email protected]>"]
readme = "README.md"
Expand Down
4 changes: 3 additions & 1 deletion web/b3desk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from .utils import enum_converter
from .utils import model_converter

__version__ = "1.2.5"
__version__ = "1.2.6"

LANGUAGES = ["en", "fr"]

Expand Down Expand Up @@ -193,6 +193,7 @@ def internal_error(error):

def setup_endpoints(app):
with app.app_context():
import b3desk.commands
import b3desk.endpoints.api
import b3desk.endpoints.join
import b3desk.endpoints.meeting_files
Expand All @@ -204,6 +205,7 @@ def setup_endpoints(app):
app.register_blueprint(b3desk.endpoints.meetings.bp)
app.register_blueprint(b3desk.endpoints.api.bp)
app.register_blueprint(b3desk.endpoints.meeting_files.bp)
app.register_blueprint(b3desk.commands.bp)


def setup_oidc(app):
Expand Down
19 changes: 19 additions & 0 deletions web/b3desk/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import click
from flask import Blueprint
from flask import current_app

bp = Blueprint("commands", __name__, cli_group=None)


@bp.cli.command("get-apps-id")
@click.argument("email")
def get_apps_id(email):
from b3desk.models.users import get_secondary_identity_provider_id_from_email

try:
secondary_id = get_secondary_identity_provider_id_from_email(email)
current_app.logger.info(
"ID from secondary identity provider for email %s: %s", email, secondary_id
)
except Exception as e:
current_app.logger.error(e)
131 changes: 121 additions & 10 deletions web/b3desk/models/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,125 @@ def make_nextcloud_credentials_request(url, payload, headers):
return None


def get_user_nc_credentials(username):
class MissingToken(Exception):
"""Exception raised if unable to get token.
Attributes:
message -- explanation of the error
"""

def __init__(self, message="No token given for the B3Desk instance"):
self.message = message
super().__init__(self.message)


class TooManyUsers(Exception):
"""Exception raised if email returns more than one user.
Attributes:
message -- explanation of the error
"""

def __init__(self, message="More than one user is using this email"):
self.message = message
super().__init__(self.message)


class NoUserFound(Exception):
"""Exception raised if email returns no user.
Attributes:
message -- explanation of the error
"""

def __init__(self, message="No user with this email was found"):
self.message = message
super().__init__(self.message)


def get_secondary_identity_provider_token():
return requests.post(
f"{current_app.config['SECONDARY_IDENTITY_PROVIDER_URI']}/auth/realms/{current_app.config['SECONDARY_IDENTITY_PROVIDER_REALM']}/protocol/openid-connect/token",
headers={"Content-Type": "application/x-www-form-urlencoded"},
data={
"grant_type": "client_credentials",
"client_id": f"{current_app.config['SECONDARY_IDENTITY_PROVIDER_CLIENT_ID']}",
"client_secret": f"{current_app.config['SECONDARY_IDENTITY_PROVIDER_CLIENT_SECRET']}",
},
)


def get_secondary_identity_provider_users_from_email(email, access_token):
return requests.get(
f"{current_app.config['SECONDARY_IDENTITY_PROVIDER_URI']}/auth/admin/realms/{current_app.config['SECONDARY_IDENTITY_PROVIDER_REALM']}/users",
headers={
"Authorization": f"Bearer {access_token}",
"cache-control": "no-cache",
},
params={"email": email},
)


def get_secondary_identity_provider_id_from_email(email):
try:
token_response = get_secondary_identity_provider_token()
token_response.raise_for_status()
except requests.exceptions.HTTPError as exception:
current_app.logger.warning(
"Get token request error: %s, %s", exception, token_response.text
)
raise exception

try:
access_token = token_response.json()["access_token"]
if not access_token:
raise MissingToken(
f"No token given for the B3Desk instance, {token_response}"
)
except (AttributeError, KeyError):
raise MissingToken(f"No token given for the B3Desk instance, {token_response}")

try:
users_response = get_secondary_identity_provider_users_from_email(
email=email, access_token=access_token
)
users_response.raise_for_status()
except requests.exceptions.HTTPError as exception:
current_app.logger.warning(
"Get user from email request error: %s, %s", exception, users_response.text
)
raise exception
found_users = users_response.json()
if (user_count := len(found_users)) > 1:
raise TooManyUsers(f"There are {user_count} users with the email {email}")
elif user_count < 1:
raise NoUserFound(f"There are no users with the email {email}")

[user] = found_users
return user["username"]


def get_user_nc_credentials(preferred_username="", email=""):
if (
not current_app.config["NC_LOGIN_API_KEY"]
or not current_app.config["NC_LOGIN_API_URL"]
or not current_app.config["FILE_SHARING"]
or not username
or not (preferred_username or email)
):
current_app.logger.info(
"File sharing deactivated or unable to perform, no connection to Nextcloud instance"
)
return {"nctoken": None, "nclocator": None, "nclogin": None}

payload = {"username": username}
nc_username = preferred_username
if current_app.config["SECONDARY_IDENTITY_PROVIDER_ENABLED"] and email:
try:
nc_username = get_secondary_identity_provider_id_from_email(email=email)
except requests.exceptions.HTTPError:
pass
except (TooManyUsers, NoUserFound) as e:
current_app.logger.warning(e)
payload = {"username": nc_username}
headers = {"X-API-KEY": current_app.config["NC_LOGIN_API_KEY"]}
current_app.logger.info(
"Retrieve NC credentials from NC_LOGIN_API_URL %s "
Expand Down Expand Up @@ -103,12 +209,17 @@ def update_user_nc_credentials(user, user_info):
if current_app.config["FILE_SHARING"]
else None
)
data = get_user_nc_credentials(preferred_username)
if (
preferred_username is None
or data["nclocator"] is None
or data["nctoken"] is None
):

if current_app.config["SECONDARY_IDENTITY_PROVIDER_ENABLED"]:
data = get_user_nc_credentials(
email=(
user_info.get("email") if current_app.config["FILE_SHARING"] else None
)
)
else:
data = get_user_nc_credentials(preferred_username=preferred_username)

if data["nclogin"] is None or data["nclocator"] is None or data["nctoken"] is None:
current_app.logger.info(
"No new Nextcloud enroll needed for user %s with those data %s", user, data
)
Expand All @@ -119,7 +230,7 @@ def update_user_nc_credentials(user, user_info):

user.nc_locator = data["nclocator"]
user.nc_token = data["nctoken"]
user.nc_login = preferred_username
user.nc_login = data["nclogin"]
user.nc_last_auto_enroll = nc_last_auto_enroll
return True

Expand Down
6 changes: 3 additions & 3 deletions web/requirements.app.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ prompt-toolkit==3.0.43 ; python_version >= "3.9" and python_version < "4.0"
psycopg2==2.9.9 ; python_version >= "3.9" and python_version < "4.0"
pycparser==2.22 ; python_version >= "3.9" and python_version < "4.0" and platform_python_implementation != "PyPy"
pycryptodomex==3.20.0 ; python_version >= "3.9" and python_version < "4.0"
pydantic-core==2.18.1 ; python_version >= "3.9" and python_version < "4.0"
pydantic-core==2.18.2 ; python_version >= "3.9" and python_version < "4.0"
pydantic-settings==2.2.1 ; python_version >= "3.9" and python_version < "4.0"
pydantic==2.7.0 ; python_version >= "3.9" and python_version < "4.0"
pydantic==2.7.1 ; python_version >= "3.9" and python_version < "4.0"
pyjwkest==1.4.2 ; python_version >= "3.9" and python_version < "4.0"
python-dateutil==2.9.0.post0 ; python_version >= "3.9" and python_version < "4.0"
python-dotenv==1.0.1 ; python_version >= "3.9" and python_version < "4.0"
python-slugify==8.0.4 ; python_version >= "3.9" and python_version < "4.0"
pytz==2024.1 ; python_version >= "3.9" and python_version < "4.0"
redis==5.0.3 ; python_version >= "3.9" and python_version < "4.0"
redis==5.0.4 ; python_version >= "3.9" and python_version < "4.0"
requests==2.31.0 ; python_version >= "3.9" and python_version < "4.0"
six==1.16.0 ; python_version >= "3.9" and python_version < "4.0"
sqlalchemy-json==0.7.0 ; python_version >= "3.9" and python_version < "4.0"
Expand Down
16 changes: 8 additions & 8 deletions web/requirements.dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ cffi==1.16.0 ; python_version >= "3.9" and python_version < "4" and platform_pyt
cfgv==3.4.0 ; python_version >= "3.9" and python_version < "4.0"
click==8.1.7 ; python_version >= "3.9" and python_version < "4.0"
colorama==0.4.6 ; python_version >= "3.9" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows")
coverage==7.4.4 ; python_version >= "3.9" and python_version < "4.0"
coverage[toml]==7.4.4 ; python_version >= "3.9" and python_version < "4.0"
coverage==7.5.0 ; python_version >= "3.9" and python_version < "4.0"
coverage[toml]==7.5.0 ; python_version >= "3.9" and python_version < "4.0"
cryptography==42.0.5 ; python_version >= "3.9" and python_version < "4"
cssselect==1.2.0 ; python_version >= "3.9" and python_version < "4.0"
defusedxml==0.7.1 ; python_version >= "3.9" and python_version < "4.0"
distlib==0.3.8 ; python_version >= "3.9" and python_version < "4.0"
exceptiongroup==1.2.1 ; python_version >= "3.9" and python_version < "3.11"
faker==24.11.0 ; python_version >= "3.9" and python_version < "4.0"
faker==24.14.0 ; python_version >= "3.9" and python_version < "4.0"
filelock==3.13.4 ; python_version >= "3.9" and python_version < "4.0"
flake8==7.0.0 ; python_version >= "3.9" and python_version < "4.0"
flask-webtest==0.1.4 ; python_version >= "3.9" and python_version < "4.0"
flask-wtf==1.2.1 ; python_version >= "3.9" and python_version < "4.0"
flask==3.0.3 ; python_version >= "3.9" and python_version < "4.0"
freezegun==1.4.0 ; python_version >= "3.9" and python_version < "4.0"
freezegun==1.5.0 ; python_version >= "3.9" and python_version < "4.0"
identify==2.5.36 ; python_version >= "3.9" and python_version < "4.0"
importlib-metadata==7.1.0 ; python_version >= "3.9" and python_version < "3.10"
iniconfig==2.0.0 ; python_version >= "3.9" and python_version < "4.0"
Expand All @@ -35,16 +35,16 @@ markupsafe==2.1.5 ; python_version >= "3.9" and python_version < "4.0"
mccabe==0.7.0 ; python_version >= "3.9" and python_version < "4.0"
nodeenv==1.8.0 ; python_version >= "3.9" and python_version < "4.0"
packaging==24.0 ; python_version >= "3.9" and python_version < "4.0"
platformdirs==4.2.0 ; python_version >= "3.9" and python_version < "4.0"
platformdirs==4.2.1 ; python_version >= "3.9" and python_version < "4.0"
pluggy==1.5.0 ; python_version >= "3.9" and python_version < "4.0"
portpicker==1.6.0 ; python_version >= "3.9" and python_version < "4.0"
pre-commit==3.7.0 ; python_version >= "3.9" and python_version < "4.0"
psutil==5.9.8 ; python_version >= "3.9" and python_version < "4.0"
pycodestyle==2.11.1 ; python_version >= "3.9" and python_version < "4.0"
pycparser==2.22 ; python_version >= "3.9" and python_version < "4" and platform_python_implementation != "PyPy"
pydantic-core==2.18.1 ; python_version >= "3.9" and python_version < "4.0"
pydantic-core==2.18.2 ; python_version >= "3.9" and python_version < "4.0"
pydantic-settings==2.2.1 ; python_version >= "3.9" and python_version < "4.0"
pydantic==2.7.0 ; python_version >= "3.9" and python_version < "4.0"
pydantic==2.7.1 ; python_version >= "3.9" and python_version < "4.0"
pyflakes==3.2.0 ; python_version >= "3.9" and python_version < "4.0"
pyquery==2.0.0 ; python_version >= "3.9" and python_version < "4.0"
pytest-cov==5.0.0 ; python_version >= "3.9" and python_version < "4.0"
Expand All @@ -62,7 +62,7 @@ smtpdfix==0.5.2 ; python_version >= "3.9" and python_version < "4"
soupsieve==2.5 ; python_version >= "3.9" and python_version < "4"
tomli==2.0.1 ; python_version >= "3.9" and python_full_version <= "3.11.0a6"
typing-extensions==4.11.0 ; python_version >= "3.9" and python_version < "4.0"
virtualenv==20.25.3 ; python_version >= "3.9" and python_version < "4.0"
virtualenv==20.26.0 ; python_version >= "3.9" and python_version < "4.0"
waitress==3.0.0 ; python_version >= "3.9" and python_version < "4"
webob==1.8.7 ; python_version >= "3.9" and python_version < "4"
webtest==3.0.0 ; python_version >= "3.9" and python_version < "4"
Expand Down
6 changes: 3 additions & 3 deletions web/requirements.doc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@ prompt-toolkit==3.0.43 ; python_version >= "3.9" and python_version < "4.0"
psycopg2==2.9.9 ; python_version >= "3.9" and python_version < "4.0"
pycparser==2.22 ; python_version >= "3.9" and python_version < "4.0" and platform_python_implementation != "PyPy"
pycryptodomex==3.20.0 ; python_version >= "3.9" and python_version < "4.0"
pydantic-core==2.18.1 ; python_version >= "3.9" and python_version < "4.0"
pydantic-core==2.18.2 ; python_version >= "3.9" and python_version < "4.0"
pydantic-settings==2.2.1 ; python_version >= "3.9" and python_version < "4.0"
pydantic==2.7.0 ; python_version >= "3.9" and python_version < "4.0"
pydantic==2.7.1 ; python_version >= "3.9" and python_version < "4.0"
pygments==2.17.2 ; python_version >= "3.9" and python_version < "4.0"
pyjwkest==1.4.2 ; python_version >= "3.9" and python_version < "4.0"
python-dateutil==2.9.0.post0 ; python_version >= "3.9" and python_version < "4.0"
python-dotenv==1.0.1 ; python_version >= "3.9" and python_version < "4.0"
python-slugify==8.0.4 ; python_version >= "3.9" and python_version < "4.0"
pytz==2024.1 ; python_version >= "3.9" and python_version < "4.0"
pyyaml==6.0.1 ; python_version >= "3.9" and python_version < "4.0"
redis==5.0.3 ; python_version >= "3.9" and python_version < "4.0"
redis==5.0.4 ; python_version >= "3.9" and python_version < "4.0"
requests==2.31.0 ; python_version >= "3.9" and python_version < "4.0"
six==1.16.0 ; python_version >= "3.9" and python_version < "4.0"
snowballstemmer==2.2.0 ; python_version >= "3.9" and python_version < "4.0"
Expand Down
17 changes: 17 additions & 0 deletions web/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def configuration(tmp_path, iam_server, iam_client, smtpd):
"OIDC_CLIENT_AUTH_METHOD": iam_client.token_endpoint_auth_method,
"OIDC_SCOPES": iam_client.scope,
"OIDC_USERINFO_HTTP_METHOD": "GET",
"SECONDARY_IDENTITY_PROVIDER_ENABLED": False,
"UPLOAD_DIR": str(tmp_path),
"TMP_DOWNLOAD_DIR": str(tmp_path),
"RECORDING": True,
Expand Down Expand Up @@ -287,3 +288,19 @@ def cloud_service_response(mocker, webdav_server, request):
@pytest.fixture
def jpg_file_content():
return b"\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00\xff\xdb\x00C\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc2\x00\x0b\x08\x00\x01\x00\x01\x01\x01\x11\x00\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00\x01?\x10"


class ValidToken:
def raise_for_status():
pass

def json():
return {"access_token": "valid_token"}


@pytest.fixture
def valid_secondary_identity_token(mocker):
mocker.patch(
"b3desk.models.users.get_secondary_identity_provider_token",
return_value=ValidToken,
)
Loading

0 comments on commit 80d76f6

Please sign in to comment.