Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ install_requires =
click-params
cssmin
cssutils
dectate@git+https://github.com/morepath/dectate.git#egg=dectate
dicttoxml
docxtpl
dogpile.cache
Expand All @@ -88,6 +89,7 @@ install_requires =
html5lib
humanize
icalendar
importscan@git+https://github.com/morepath/importscan.git#egg=importscan
isodate
itsdangerous
kerberos
Expand All @@ -99,11 +101,11 @@ install_requires =
lxml
markupsafe
mistletoe>=1.2.0
more.content_security
more.transaction
more.webassets
more.content_security@git+https://github.com/morepath/more.content_security.git#egg=more.content_security
more.transaction@git+https://github.com/morepath/more.transaction.git#egg=more.transaction
more.webassets@git+https://github.com/morepath/more.webassets.git#egg=more.webassets
more_itertools
morepath
morepath@git+https://github.com/morepath/morepath.git#egg=morepath
msal
niquests[speedups]
oauthlib
Expand Down Expand Up @@ -139,6 +141,7 @@ install_requires =
rapidfuzz
rcssmin
redis
reg@git+https://github.com/morepath/reg.git#egg=reg
reportlab
# FIXME: It may be worth to create a fork for niquests or manually
# make use of oauthlib, for the features we need. For now
Expand Down
3 changes: 2 additions & 1 deletion src/onegov/agency/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@


from typing import Any
from typing import Self
from typing import TYPE_CHECKING

if TYPE_CHECKING:
Expand All @@ -36,7 +37,7 @@

class AgencyApp(TownApp):

request_class = AgencyRequest
request_class: type[AgencyRequest[Self]] = AgencyRequest

if TYPE_CHECKING:
# FIXME: Maybe we should consider just raising an exception
Expand Down
7 changes: 4 additions & 3 deletions src/onegov/agency/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
from onegov.town6.request import TownRequest


from typing import TypeVar
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from onegov.agency.app import AgencyApp


class AgencyRequest(TownRequest):
AppT = TypeVar('AppT', bound='AgencyApp', default='AgencyApp', covariant=True)


app: AgencyApp
class AgencyRequest(TownRequest[AppT]):

@cached_property
def current_role(self) -> str | None:
Expand Down
2 changes: 1 addition & 1 deletion src/onegov/agency/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@

from typing import TYPE_CHECKING
if TYPE_CHECKING:
from morepath.authentication import Identity
from morepath.authentication import NoIdentity
from onegov.core import Identity
from onegov.core.security.roles import Intent
from sqlalchemy.orm import Session

Expand Down
3 changes: 2 additions & 1 deletion src/onegov/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@

from onegov.core.framework import Framework
from onegov.core.filestorage import get_filestorage_file # noqa: F401
from onegov.core.identity import OneGovIdentity as Identity

# include the filters module so they get picked up by webassets
from onegov.core import filters # noqa: F401

__all__ = ['Framework', 'log']
__all__ = ['Framework', 'Identity', 'log']
43 changes: 21 additions & 22 deletions src/onegov/core/directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import os.path

from dectate import Action, Query, convert_dotted_name # type:ignore[attr-defined]
from dectate import Action, Query, convert_dotted_name
from itertools import count

from morepath import render_json, Request
Expand All @@ -18,7 +18,6 @@

from typing import Any, ClassVar, TYPE_CHECKING
if TYPE_CHECKING:
from _typeshed import StrOrBytesPath, StrPath
from collections.abc import Callable, Mapping
from webob import Response
from wtforms import Form
Expand Down Expand Up @@ -60,12 +59,12 @@ def handle_form(self, request, form):
"""
def __init__[RequestT: CoreRequest](
self,
model: type | str,
model: type,
form: type[Form] | Callable[[Any, RequestT], type[Form]],
render: Callable[[Any, RequestT], Response] | str | None = None,
template: StrOrBytesPath | None = None,
load: Callable[[RequestT], Any] | str | None = None,
permission: object | str | None = None,
render: Callable[[Any, RequestT], Response] | None = None,
template: str | None = None,
load: Callable[[RequestT], Any] | None = None,
permission: object | None = None,
internal: bool = False,
pass_model: bool = False,
**predicates: Any
Expand Down Expand Up @@ -208,7 +207,7 @@ def __init__(
def identifier(self, **kw: Any) -> int:
return self.name

def perform( # type:ignore[override]
def perform(
self,
func: Callable[[CoreRequest], Any],
cronjob_registry: Bunch
Expand All @@ -235,13 +234,13 @@ def __init__(self, name: str, title: str) -> None:
self.name = name
self.title = title

def identifier( # type:ignore[override]
def identifier(
self,
analytics_provider_registry: dict[str, AnalyticsProvider]
) -> str:
return self.name

def perform( # type:ignore[override]
def perform(
self,
func: type[AnalyticsProvider],
analytics_provider_registry: dict[str, type[AnalyticsProvider]]
Expand All @@ -267,13 +266,13 @@ class StaticDirectoryAction(Action):
def __init__(self) -> None:
self.name = next(self.counter)

def identifier( # type:ignore[override]
def identifier(
self,
staticdirectory_registry: Bunch
) -> int:
return self.name

def perform( # type:ignore[override]
def perform(
self,
func: Callable[..., Any],
staticdirectory_registry: Bunch
Expand Down Expand Up @@ -317,13 +316,13 @@ def get_template_variables(request):
def __init__(self) -> None:
self.section = 'templatevariables'

def identifier( # type:ignore[override]
def identifier(
self,
setting_registry: SettingRegistry
) -> str:
return self.section

def perform( # type:ignore[override]
def perform(
self,
func: Callable[[CoreRequest], dict[str, Any]],
setting_registry: SettingRegistry
Expand Down Expand Up @@ -351,7 +350,7 @@ def __init__(self, section: str) -> None:
def identifier(self, **kw: Any) -> str:
return self.section

def perform( # type: ignore[override]
def perform(
self,
obj: Callable[[], Mapping[str, Any]],
setting_registry: SettingRegistry
Expand Down Expand Up @@ -388,21 +387,21 @@ class Layout(Action):
def __init__(self, model: type) -> None:
self.model = model

def identifier( # type:ignore[override]
def identifier(
self,
app_class: type[Framework]
) -> str:
return str(self.model)

def perform( # type:ignore[override]
def perform(
self,
obj: type[CoreLayout],
app_class: type[Framework]
) -> None:

layout_class = obj
# `lambda self, obj, request` is required to match the signature
app_class.get_layout.register( # type:ignore[attr-defined]
app_class.get_layout.register(
lambda self, obj, request: layout_class(obj, request),
model=self.model)

Expand Down Expand Up @@ -431,10 +430,10 @@ class ExtendedJsonAction(JsonAction):

def __init__(
self,
model: type | str,
render: Callable[[Any, Any], Response] | str | None = None,
template: StrPath | None = None,
load: Callable[[Any], Any] | str | None = None,
model: type,
render: Callable[[Any, Any], Response] | None = None,
template: str | None = None,
load: Callable[[Any], Any] | None = None,
permission: object = None,
internal: bool = False,
open_data: bool = False,
Expand Down
19 changes: 10 additions & 9 deletions src/onegov/core/framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class MyApplication(Framework):
from onegov.core.orm.cache import OrmCacheApp
from onegov.core.orm.observer import ScopedPropertyObserver
from onegov.core.request import CoreRequest
from onegov.core.identity import OneGovIdentity as Identity
from onegov.core.utils import batched, PostThread
from onegov.server import Application as ServerApplication
from onegov.server.utils import load_class
Expand All @@ -70,15 +71,14 @@ class MyApplication(Framework):
from webob.exc import HTTPConflict, HTTPServiceUnavailable


from typing import overload, Any, Literal, TYPE_CHECKING
from typing import overload, Any, Literal, Self, TYPE_CHECKING
if TYPE_CHECKING:
from _typeshed import StrPath
from _typeshed.wsgi import WSGIApplication, WSGIEnvironment, StartResponse
from collections.abc import Callable, Iterable
from email.headerregistry import Address
from fs.base import FS, SubFS
from gettext import GNUTranslations
from morepath.request import Request
from morepath.settings import SettingRegistry
from sqlalchemy.orm import Session
from translationstring import _ChameleonTranslate
Expand All @@ -96,7 +96,7 @@ class MyApplication(Framework):
# This should be in more.webassets:
# https://github.com/morepath/more.webassets/blob/master/more/webassets/core.py#L55
if not WebassetsApp.dectate._directives[0][0].kw:
from morepath.core import excview_tween_factory # type:ignore
from morepath.core import excview_tween_factory
WebassetsApp.dectate._directives[0][0].kw['over'] = excview_tween_factory


Expand All @@ -109,7 +109,7 @@ class Framework(
):
""" Baseclass for Morepath OneGov applications. """

request_class: type[Request] = CoreRequest
request_class: type[CoreRequest[Self]] = CoreRequest

#: holds the database connection string, *if* there is a database connected
dsn: str | None = None
Expand All @@ -129,7 +129,7 @@ class Framework(
replace_setting = directive(directives.ReplaceSettingAction)
replace_setting_section = directive(directives.ReplaceSettingSectionAction)
layout = directive(directives.Layout)
json = directive(directives.ExtendedJsonAction) # type: ignore[assignment]
json = directive(directives.ExtendedJsonAction)

#: sets the same-site cookie directive, (may need removal inside iframes)
same_site_cookie_policy: str | None = 'Lax'
Expand Down Expand Up @@ -798,7 +798,7 @@ def object_by_path(
'SERVER_NAME': '',
'SERVER_PORT': '',
'SERVER_PROTOCOL': 'https'
}, app=self)
}, app=self) # type: ignore[arg-type]

obj = resolve_model(request)

Expand Down Expand Up @@ -834,12 +834,13 @@ def permission_by_view(
)

try:
action, _handler = next(query(self.__class__))
action, _handler = next(iter(query(self.__class__)))
except (StopIteration, RuntimeError) as exception:
raise KeyError(
'{!r} has no view named {}'.format(model, view_name)
) from exception

assert hasattr(action, 'permission')
return action.permission

@cached_property
Expand Down Expand Up @@ -1348,12 +1349,12 @@ def application_bound_identity(
uid: str,
groupids: frozenset[str],
role: str
) -> morepath.authentication.Identity:
) -> Identity:
""" Returns a new morepath identity for the given userid, group and
role, bound to this application.

"""
return morepath.authentication.Identity(
return Identity(
userid, uid=uid, groupids=groupids, role=role,
application_id=self.application_id_hash
)
Expand Down
36 changes: 36 additions & 0 deletions src/onegov/core/identity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import annotations

from morepath import Identity


from typing import TYPE_CHECKING


class OneGovIdentity(Identity):

userid: str # email
uid: str # actual user id
groupids: frozenset[str]
role: str
application_id: str

def __init__(
self,
userid: str,
*,
uid: str,
groupids: frozenset[str],
role: str,
application_id: str
) -> None:
super().__init__(
userid,
uid=uid,
groupids=groupids,
role=role,
application_id=application_id
)

# NOTE: Pretend arbitrary attribute access is disabled
if TYPE_CHECKING:
__getattr__ = None # type: ignore
Loading