Skip to content

Commit

Permalink
Merge branch 'main' into 2674_saml_meeting_matching
Browse files Browse the repository at this point in the history
  • Loading branch information
hjanott authored Nov 12, 2024
2 parents 0fc8161 + cb07383 commit db7043d
Show file tree
Hide file tree
Showing 44 changed files with 1,029 additions and 164 deletions.
4 changes: 1 addition & 3 deletions docs/actions/agenda_item.create.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
duration: number; // in seconds
weight: number;
tag_ids: Id[];
moderator_notes: HTML;
}
```

Expand All @@ -23,5 +22,4 @@ item or the content object cannot have an agenda item (see available collections
`models.yml`). `tag_ids` must be from the same meeting.

## Permissions
The request user needs `agenda_item.can_manage_moderator_notes` to set `moderator_notes` and
`agenda_item.can_manage` for all other fields.
The request user needs `agenda_item.can_manage`.
5 changes: 2 additions & 3 deletions docs/actions/agenda_item.update.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
duration: number; // in minutes
weight: number;
tag_ids: Id[];
moderator_notes: HTML;
}
```

Expand All @@ -21,5 +20,5 @@ Updates the agenda item. `tag_ids` must be from the same meeting.
The `type` attribute of one `agenda_item` must be one of [`common`, `internal`, `hidden`].

## Permissions
The request user needs `agenda_item.can_manage_moderator_notes` to set `moderator_notes` and
`agenda_item.can_manage` for all other fields.
The request user needs `agenda_item.can_manage`.

2 changes: 1 addition & 1 deletion docs/actions/group.update.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ Updates the group. Permissions are restricted to the following enum: https://git
If the group is the meetings anonymous group, the name may not be changed and the permissions have to be in the following whitelist:
- agenda_item.can_see,
- agenda_item.can_see_internal,
- agenda_item.can_see_moderator_notes,
- assignment.can_see,
- list_of_speakers.can_see,
- list_of_speakers.can_see_moderator_notes,
- mediafile.can_see,
- meeting.can_see_autopilot,
- meeting.can_see_frontpage,
Expand Down
4 changes: 3 additions & 1 deletion docs/actions/list_of_speakers.update.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
// Optional
closed: boolean;
moderator_notes: HTML;
}
```

## Action
Updates a list of speakers.

## Permissions
The request user needs `list_of_speakers.can_manage`.
The request user needs `list_of_speakers.can_manage_moderator_notes` to set `moderator_notes` and
`list_of_speakers.can_manage` for all other fields.
4 changes: 2 additions & 2 deletions docs/actions/user.forget_password.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

## Action

A user can change their password by this action without being authenticated. To perform a changing of their password the user enters their email-address. The client then sends a hard-coded reset email to the given email-address (as `email`). TODO: translations
A user can change their password by this action without being authenticated. To perform a changing of their password the user enters their email-address (case insensitive). The client then sends a hard-coded reset email to the given email-address (as `email`). TODO: translations

Regardless if the email-address is used by a user, the client shows the user a successful message (for example "An email was successfully sent to the given email-address"). This is necessary to avoid filtering which email-address is used by an OpenSlides-user.

In the case that an email-address is used by a user, an email is sent to that email-address with the given text including a link to set a new password. If multiple users use the same email address, one email is sent per user.
In the case that an email-address is used by a user, an email is sent to that email-address of that user including a link to set a new password. If multiple users use the same email address (case insensitive), one email is sent per user (case-sensitive).
The link redirects a user to `<domain>/login/forget-password-confirm?user_id=<user_id>&token=<token>`.
As you can see, the user_id of the user and a token are given as query-parameters. The token is a [jsonwebtoken](https://jwt.io/) (specified by [RFC7519](https://datatracker.ietf.org/doc/html/rfc7519)), which is self-contained and up to ten minutes valid. The user_id and the email-address (as `email`) are given as payload to the token. Furthermore, the token is signed. The secret to sign the token is the secret which is used to sign `access_token`s. The algorithm `HS256` will be used. The token is given as a base64-encoded string.

Expand Down
10 changes: 10 additions & 0 deletions docs/actions/user.send_invitation_email.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ Use `meeting/users_email_subject` (if a meeting is given by `meeting_id`); other
- `event_name` as `meeting/name` (if a meeting is given, otherwise `organization/name`) and
- `username` as `user/username`
- `name` as the users short name
- `title` as `user/title`
- `given_name` as `user/first_name`
- `surname` as `user/last_name`
- `groups` as a listing of the names of the users groups in the meeting
- `structure_levels` as a listing of the names of the users structure_levels in the meeting
to be replaced in the template (see [here](https://github.com/OpenSlides/OpenSlides/blob/7315626e18c0515b6ff61551c705156cbd5056cb/server/openslides/users/models.py#L266))

### Body
Expand All @@ -44,6 +49,11 @@ It does an equal string formatting as for the subject. Use `meeting/users_email
- `url` as `meeting/users_pdf_url` (this can only be used, if a meeting is given; otherwise it's the `organization/url`)
- `username` as `user/username`
- `password` as `user/default_password`
- `title` as `user/title`
- `first_name` as `user/first_name`
- `last_name` as `user/last_name`
- `groups` as a listing of the names of the users groups in the meeting.
- `structure_levels` as a listing of the names of the users structure_levels.

### Unknown keywords in Subject or Body
Sending email is no longer refused, the wrong keyword will be injected instead.
Expand Down
5 changes: 4 additions & 1 deletion entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#!/bin/bash

source scripts/export_datastore_variables.sh
scripts/wait.sh $DATASTORE_WRITER_HOST $DATASTORE_WRITER_PORT

if [ ! $ANONYMOUS_ONLY ]; then
scripts/wait.sh $DATASTORE_WRITER_HOST $DATASTORE_WRITER_PORT
fi

exec "$@"
2 changes: 1 addition & 1 deletion global/data/example-data.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_migration_index": 60,
"_migration_index": 62,
"gender":{
"1":{
"id": 1,
Expand Down
2 changes: 1 addition & 1 deletion global/data/initial-data.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_migration_index": 60,
"_migration_index": 62,
"gender":{
"1":{
"id": 1,
Expand Down
2 changes: 1 addition & 1 deletion global/meta
Submodule meta updated 3 files
+2 −2 dev/requirements.txt
+21 −15 models.yml
+2 −3 permission.yml
1 change: 1 addition & 0 deletions openslides_backend/action/action_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ def initial_action_worker_write(self) -> str:
"state": ActionWorkerState.RUNNING,
"created": self.start_time,
"timestamp": current_time,
"user_id": self.user_id,
},
)
],
Expand Down
4 changes: 1 addition & 3 deletions openslides_backend/action/actions/agenda_item/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@
)
from ...util.default_schema import DefaultSchema
from ...util.register import register_action
from .permission_mixin import AgendaItemPermissionMixin


@register_action("agenda_item.create")
class AgendaItemCreate(AgendaItemPermissionMixin, CreateActionWithInferredMeeting):
class AgendaItemCreate(CreateActionWithInferredMeeting):
"""
Action to create agenda items.
"""
Expand All @@ -29,7 +28,6 @@ class AgendaItemCreate(AgendaItemPermissionMixin, CreateActionWithInferredMeetin
"duration",
"weight",
"tag_ids",
"moderator_notes",
],
)
permission = Permissions.AgendaItem.CAN_MANAGE
Expand Down
25 changes: 0 additions & 25 deletions openslides_backend/action/actions/agenda_item/permission_mixin.py

This file was deleted.

4 changes: 1 addition & 3 deletions openslides_backend/action/actions/agenda_item/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
from ...util.default_schema import DefaultSchema
from ...util.register import register_action
from ...util.typing import ActionData
from .permission_mixin import AgendaItemPermissionMixin


@register_action("agenda_item.update")
class AgendaItemUpdate(AgendaItemPermissionMixin, UpdateAction):
class AgendaItemUpdate(UpdateAction):
"""
Action to update agenda items.
"""
Expand All @@ -25,7 +24,6 @@ class AgendaItemUpdate(AgendaItemPermissionMixin, UpdateAction):
"weight",
"tag_ids",
"duration",
"moderator_notes",
]
)
permission = Permissions.AgendaItem.CAN_MANAGE
Expand Down
20 changes: 19 additions & 1 deletion openslides_backend/action/actions/list_of_speakers/update.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from typing import Any

from ....models.models import ListOfSpeakers
from ....permissions.permission_helper import has_perm
from ....permissions.permissions import Permissions
from ....shared.exceptions import MissingPermission
from ...generics.update import UpdateAction
from ...util.default_schema import DefaultSchema
from ...util.register import register_action
Expand All @@ -13,6 +17,20 @@ class ListOfSpeakersUpdateAction(UpdateAction):

model = ListOfSpeakers()
schema = DefaultSchema(ListOfSpeakers()).get_update_schema(
optional_properties=["closed"]
optional_properties=["closed", "moderator_notes"]
)
permission = Permissions.ListOfSpeakers.CAN_MANAGE

def check_permissions(self, instance: dict[str, Any]) -> None:
if "moderator_notes" in instance:
perm = Permissions.ListOfSpeakers.CAN_MANAGE_MODERATOR_NOTES
if not has_perm(
self.datastore,
self.user_id,
perm,
self.get_meeting_id(instance),
):
raise MissingPermission(perm)
if len(instance) == 2:
return
super().check_permissions(instance)
2 changes: 1 addition & 1 deletion openslides_backend/action/actions/meeting/clone.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def check_permissions(self, instance: dict[str, Any]) -> None:
MeetingPermissionMixin.check_permissions(self, instance)

def update_instance(self, instance: dict[str, Any]) -> dict[str, Any]:
meeting_json = export_meeting(self.datastore, instance["meeting_id"])
meeting_json = export_meeting(self.datastore, instance["meeting_id"], True)
instance["meeting"] = meeting_json
additional_user_ids = instance.pop("user_ids", None) or []
additional_admin_ids = instance.pop("admin_ids", None) or []
Expand Down
8 changes: 4 additions & 4 deletions openslides_backend/action/actions/user/forget_password.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ def get_updated_instances(self, action_data: ActionData) -> ActionData:
raise ActionException(f"'{email}' is not a valid email adress.")

# search for users with email
filter_ = FilterOperator("email", "=", email)
filter_ = FilterOperator("email", "~=", email)
results = self.datastore.filter(
self.model.collection, filter_, ["id", "username", "saml_id"]
self.model.collection, filter_, ["id", "username", "saml_id", "email"]
)

organization = self.datastore.get(
Expand All @@ -79,11 +79,11 @@ def get_updated_instances(self, action_data: ActionData) -> ActionData:
mail_client,
self.logger,
EmailSettings.default_from_email,
email,
user["email"],
self.PW_FORGET_EMAIL_SUBJECT + f": {username}",
self.get_email_body(
user["id"],
self.get_token(user["id"], email),
self.get_token(user["id"], user["email"]),
user["username"],
url,
),
Expand Down
6 changes: 3 additions & 3 deletions openslides_backend/action/actions/user/participant_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,7 @@ def validate_entry(self, row: ImportRow) -> None:
entry["meeting_id"] = self.meeting_id

if isinstance(entry.get("gender"), dict):
if entry["gender"].get("info") != ImportState.WARNING:
entry["gender_id"] = entry["gender"]["id"]
entry.pop("gender")
entry["gender_id"] = entry.pop("gender")

if "groups" not in entry:
raise ActionException(
Expand Down Expand Up @@ -199,6 +197,8 @@ def validate_entry(self, row: ImportRow) -> None:
entry, row["messages"], entry.get("groups", []), row
)

if entry.get("gender_id"):
entry["gender"] = entry.pop("gender_id")
entry.pop("meeting_id")
if row["state"] == ImportState.ERROR and self.import_state == ImportState.DONE:
self.import_state = ImportState.ERROR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ def validate_entry(self, entry: dict[str, Any]) -> dict[str, Any]:
)

payload_index = entry.pop("payload_index", None)
# swapping needed for get_failing_fields and setting import states not to fail
if entry.get("gender"):
entry["gender_id"] = entry.pop("gender")
failing_fields = self.permission_check.get_failing_fields(entry)
entry.pop("group_ids")
entry.pop("structure_level_ids")
Expand Down Expand Up @@ -152,6 +155,8 @@ def validate_entry(self, entry: dict[str, Any]) -> dict[str, Any]:

if payload_index:
entry["payload_index"] = payload_index
if entry.get("gender_id"):
entry["gender"] = entry.pop("gender_id")

return results

Expand Down
29 changes: 29 additions & 0 deletions openslides_backend/action/actions/user/send_invitation_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

from openslides_backend.shared.util import ONE_ORGANIZATION_FQID

from ....action.mixins.meeting_user_helper import get_meeting_user
from ....models.models import User
from ....permissions.management_levels import OrganizationManagementLevel
from ....permissions.permission_helper import (
has_organization_management_level,
has_perm,
)
from ....permissions.permissions import Permissions
from ....services.datastore.commands import GetManyRequest
from ....shared.exceptions import DatastoreException, MissingPermission
from ....shared.interfaces.write_request import WriteRequest
from ....shared.patterns import fqid_from_collection_and_id
Expand Down Expand Up @@ -214,8 +216,35 @@ def __missing__(self, key: str) -> str:
"event_name": mail_data.get("name", ""),
"name": self.get_verbose_username(user),
"username": user.get("username", ""),
"title": user.get("title", ""),
"first_name": user.get("first_name", ""),
"last_name": user.get("last_name", ""),
},
)
if meeting_id:
m_user = get_meeting_user(
self.datastore,
meeting_id,
user_id,
["structure_level_ids", "group_ids"],
)
gmr = [
GetManyRequest(coll, coll_ids, ["name"])
for coll in ["group", "structure_level"]
if m_user and (coll_ids := m_user.get(coll + "_ids"))
]
gm_result: dict[str, dict[int, dict[str, Any]]] = {}
if len(gmr):
gm_result = self.datastore.get_many(gmr, lock_result=False)
subject_format.update(
{
coll
+ "s": ", ".join(
[model["name"] for model in gm_result.get(coll, {}).values()]
)
for coll in ["group", "structure_level"]
}
)
body_dict = {
"password": user.get("default_password", ""),
"url": mail_data.get("url", ""),
Expand Down
8 changes: 5 additions & 3 deletions openslides_backend/i18n/translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@ class _Translator:
translations: dict[str, Catalog] = {}
current_language: str

def __init__(self) -> None:
def __init__(self, extra_translations: dict[str, Catalog] = {}) -> None:
# read all po files at startup
path = Path(__file__).parent / "messages"
for file in path.glob("*.po"):
with file.open("r") as f:
self.translations[file.stem] = read_po(f)
# empty catalog for en since it is not used anyway
self.translations[DEFAULT_LANGUAGE] = Catalog()
if extra_translations:
self.translations.update(extra_translations)
self.current_language = DEFAULT_LANGUAGE

def translate(self, msg: str) -> str:
translation = self.translations[self.current_language].get(msg)
if translation:
if translation and translation.string:
return translation.string
else:
return msg
Expand Down Expand Up @@ -56,7 +58,7 @@ def parse_language_header(self, lang_header: str) -> list[str]:
code = code.split("-")[0]
result.append((q, code))
# sort by quality value and return only the codes
return [t[1] for t in sorted(result)]
return [t[1] for t in sorted(result, key=lambda tup: tup[0])]


Translator = _Translator()
Expand Down
Loading

0 comments on commit db7043d

Please sign in to comment.