Compare commits
No commits in common. "c6155a40e577b66e4ca8fa6373ea1cd835169401" and "5676194e560522a4a4ed6ba470f6812234b2053d" have entirely different histories.
c6155a40e5
...
5676194e56
14 changed files with 9 additions and 129 deletions
|
|
@ -24,9 +24,6 @@ dbpassword = getenv('DBPASSWORD', 'secret')
|
|||
dbschema = getenv('DBSCHEMA', 'demo')
|
||||
|
||||
# OpenID Connect (OIDC, e.g. via zitadel) authentication settings
|
||||
authentication_method = 'cookie' # 'legacy'|'select'|'oidc'|'cookie'
|
||||
|
||||
# OpenID Connect (OIDC) authentication settings
|
||||
oidc_provider = getenv('OIDC_PROVIDER', '') #'https://instance1-abcdef.zitadel.cloud')
|
||||
oidc_client_id = getenv('OIDC_CLIENT_ID', '12345')
|
||||
oidc_params = dict(
|
||||
|
|
|
|||
|
|
@ -1,27 +1,4 @@
|
|||
<configure xmlns="http://namespaces.zope.org/zope"
|
||||
xmlns:browser="http://namespaces.zope.org/browser">
|
||||
|
||||
<browser:page
|
||||
for="zope.security.interfaces.IUnauthorized"
|
||||
name="index.html"
|
||||
class="loops.server.auth.Unauthorized"
|
||||
permission="zope.Public" />
|
||||
|
||||
<!--<browser:page
|
||||
for="zope.publisher.interfaces.INotFound"
|
||||
name="index.html"
|
||||
class="loops.browser.common.NotFound"
|
||||
permission="zope.Public" />
|
||||
|
||||
<browser:page
|
||||
for="zope.interface.common.interfaces.IException"
|
||||
name="index.html"
|
||||
class="loops.browser.common.SystemErrorView"
|
||||
permission="zope.Public" />-->
|
||||
|
||||
<!--<browser:defaultSkin name="Loops" />-->
|
||||
|
||||
<include package="loops.server" file="loginform.zcml" />
|
||||
<configure xmlns="http://namespaces.zope.org/zope">
|
||||
|
||||
</configure>
|
||||
|
||||
|
|
|
|||
|
|
@ -40,24 +40,3 @@
|
|||
</form>
|
||||
</div>
|
||||
</metal:login>
|
||||
|
||||
|
||||
<metal:loggedout define-macro="loggedout"
|
||||
i18n:domain="loops"
|
||||
tal:define="principal request/principal/id">
|
||||
<h2 i18n:translate="">Logged out</h2>
|
||||
<div>
|
||||
<p i18n:translate=""
|
||||
tal:condition="python: principal == 'zope.anybody'">Your have been successfully logged out from this application. Please login again if needed or close this browser tab</p>
|
||||
<p i18n:translate=""
|
||||
tal:condition="python: principal != 'zope.anybody'">
|
||||
You are not authorized to perform this action. However, you may login as a
|
||||
different user who is authorized.</p>
|
||||
<div class="row">
|
||||
<button i18n:translate="">
|
||||
<a i18n:translate=""
|
||||
tal:attributes="href view/baseUrl">Log in again</a></button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</metal:loggedout>
|
||||
|
|
|
|||
|
|
@ -57,24 +57,6 @@ class Logout(object):
|
|||
return nx
|
||||
|
||||
|
||||
class Loggedout(NodeView):
|
||||
|
||||
template = template
|
||||
|
||||
@Lazy
|
||||
def macro(self):
|
||||
return self.template.macros['loggedout']
|
||||
|
||||
@Lazy
|
||||
def item(self):
|
||||
return self
|
||||
|
||||
@Lazy
|
||||
def baseUrl(self):
|
||||
url = self.menu.url
|
||||
return url
|
||||
|
||||
|
||||
class Unauthorized(ConceptView):
|
||||
|
||||
isTopLevel = True
|
||||
|
|
|
|||
|
|
@ -44,11 +44,6 @@
|
|||
class="loops.browser.auth.Logout"
|
||||
permission="zope.View" />
|
||||
|
||||
<page for="loops.interfaces.INode"
|
||||
name="loggedout.html"
|
||||
class="loops.browser.auth.Loggedout"
|
||||
permission="zope.View" />
|
||||
|
||||
<!-- see also view/adapter "login.html" in section "query views" -->
|
||||
|
||||
<!-- macros -->
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@
|
|||
tal:define="results reportView/results">
|
||||
<tr>
|
||||
<th style="white-space: nowrap"
|
||||
tal:attributes="class col/cssClass"
|
||||
tal:repeat="col results/displayedColumns">
|
||||
<span tal:attributes="class col/cssClass">
|
||||
<a title="tooltip_sort_column"
|
||||
tal:define="colName col/name"
|
||||
tal:omit-tag="python:not item.isSortableColumn(tableName, colName)"
|
||||
|
|
@ -64,6 +64,7 @@
|
|||
tal:condition="src"
|
||||
tal:attributes="src src" />
|
||||
</a>
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr tal:repeat="row results"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -3,7 +3,7 @@ msgstr ""
|
|||
|
||||
"Project-Id-Version: 3.0.1\n"
|
||||
"POT-Creation-Date: 2007-05-22 12:00 CET\n"
|
||||
"PO-Revision-Date: 2026-01-09 12:00 CET\n"
|
||||
"PO-Revision-Date: 2026-01-23 12:00 CET\n"
|
||||
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
|
||||
"Language-Team: loops developers <helmutm@cy55.de>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
|
@ -604,18 +604,9 @@ msgstr "Nicht angemeldet"
|
|||
msgid "Log in"
|
||||
msgstr "Anmelden"
|
||||
|
||||
msgid "Log in again"
|
||||
msgstr "Erneut anmelden"
|
||||
|
||||
msgid "Presence"
|
||||
msgstr "Anwesenheit"
|
||||
|
||||
msgid "Logged out"
|
||||
msgstr "Abgemeldet"
|
||||
|
||||
msgid "Your have been successfully logged out from this application. Please login again if needed or close this browser tab"
|
||||
msgstr "Sie haben sich erfolgreich von dieser Anwendung abgemeldet. Bitte loggen Sie sich bei Bedarf wieder ein oder schließen Sie das Browserfenster"
|
||||
|
||||
# general
|
||||
|
||||
msgid "Actions"
|
||||
|
|
@ -1503,7 +1494,7 @@ msgid "authentication-method-legacy"
|
|||
msgstr "Klassisches Login-Verfahren"
|
||||
|
||||
msgid "authentication-method-oidc"
|
||||
msgstr "Immer mit neuem Login-Verfahren anmelden"
|
||||
msgstr "Login mit OpenID Connect"
|
||||
|
||||
msgid "authentication-method-select"
|
||||
msgstr "Verfahren beim Login auswählen"
|
||||
|
|
|
|||
|
|
@ -57,8 +57,6 @@ class MediaAsset(MediaAssetFile, ExternalFileAdapter):
|
|||
data = property(ExternalFileAdapter.getData, setData)
|
||||
|
||||
def setExternalAddress(self, addr):
|
||||
if isinstance(addr, bytes):
|
||||
addr = addr.decode('UTF-8')
|
||||
ExternalFileAdapter.setExternalAddress(self, addr)
|
||||
if addr and self.getMimeType().startswith('image/'):
|
||||
self.transform(self.rules)
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ dayTo = TrackDateField('dayTo', u'End Day',
|
|||
executionSteps=['query'])
|
||||
day = TrackDateField('day', u'Day',
|
||||
description=u'The day the work was done.',
|
||||
cssClass='left',
|
||||
cssClass='center',
|
||||
executionSteps=['sort', 'output'])
|
||||
dayStart = TrackDateField('dayStart', u'Start Day',
|
||||
description=u'The day the unit of work was started.',
|
||||
|
|
@ -190,18 +190,15 @@ dayEnd = TrackDateField('dayEnd', u'End Day',
|
|||
executionSteps=['sort', 'output'])
|
||||
timeStart = TrackTimeField('start', u'Start',
|
||||
description=u'The time the unit of work was started.',
|
||||
cssClass='noprint',
|
||||
executionSteps=['sort', 'output'])
|
||||
timeEnd = TrackTimeField('end', u'End',
|
||||
description=u'The time the unit of work was finished.',
|
||||
cssClass='noprint',
|
||||
executionSteps=['output'])
|
||||
task = TargetField('taskId', u'Task',
|
||||
description=u'The task to which work items belong.',
|
||||
executionSteps=['sort', 'output'])
|
||||
party = PartyQueryField('userName', u'Party',
|
||||
description=u'The party (usually a person) who did the work.',
|
||||
cssClass='noprint',
|
||||
fieldType='selection',
|
||||
executionSteps=['sort', 'output', 'query'])
|
||||
#partyQuery = TargetField('userName', u'Party',
|
||||
|
|
@ -232,7 +229,6 @@ partyState = PartyStateField('partyState', u'Party State',
|
|||
executionSteps=['query', 'output'])
|
||||
activity = ActivityField('activity', u'LA',
|
||||
description=u'The activity assigned to the work item.',
|
||||
cssClass='noprint',
|
||||
fieldType='selection',
|
||||
executionSteps=['query', 'sort', 'output'])
|
||||
# process
|
||||
|
|
|
|||
|
|
@ -550,8 +550,6 @@ class IndexAttributes(object):
|
|||
txt = transformToText(actx)
|
||||
if txt is not None:
|
||||
return txt
|
||||
if isinstance(actx.contentType, bytes):
|
||||
actx.contentType = actx.contentType.decode('UTF-8')
|
||||
if not actx.contentType.startswith('text'):
|
||||
return u''
|
||||
data = actx.data
|
||||
|
|
@ -615,8 +613,6 @@ def transformToText(obj, data=None, contentType=None):
|
|||
data = obj.data
|
||||
if contentType is None:
|
||||
contentType = obj.contentType
|
||||
if type(contentType) == bytes:
|
||||
contentType = contentType.decode('UTF-8')
|
||||
transform = component.queryAdapter(obj, ITextTransform, name=contentType)
|
||||
if transform is not None:
|
||||
#rfa = component.queryAdapter(IReadFile, obj)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@
|
|||
# and other authentication and authorization stuff.
|
||||
|
||||
from scopes.web.auth import oidc
|
||||
from zope.authentication.interfaces import IAuthentication, IUnauthenticatedPrincipal
|
||||
from zope.authentication.interfaces import IAuthentication
|
||||
from zope.browserpage import ViewPageTemplateFile
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.component import provideAdapter, getUtility, provideUtility
|
||||
from zope.interface import implementer, Interface
|
||||
from zope.publisher.interfaces.browser import IBrowserRequest, IBrowserPage
|
||||
|
|
@ -15,9 +14,6 @@ from zope.security.proxy import removeSecurityProxy
|
|||
|
||||
import config
|
||||
|
||||
from logging import getLogger
|
||||
logger = getLogger("loops.server.auth")
|
||||
|
||||
def registerAuthUtility(config):
|
||||
baseAuth = getUtility(IAuthentication)
|
||||
print('*** registerAuthUtility, baseAuth:', baseAuth)
|
||||
|
|
@ -42,10 +38,6 @@ class LoginPage:
|
|||
return self.authOidc()
|
||||
return self.index()
|
||||
|
||||
@Lazy
|
||||
def isAnonymous(self):
|
||||
return IUnauthenticatedPrincipal.providedBy(self.request.principal)
|
||||
|
||||
def authOidc(self):
|
||||
oidc.Authenticator(self.request).login()
|
||||
return ''
|
||||
|
|
@ -57,33 +49,12 @@ class LoginPageSelect(LoginPage):
|
|||
def showSelection(self):
|
||||
return getConfigAuthMethod() == 'cookie'
|
||||
|
||||
def authMethodCookieString(self):
|
||||
domain = getattr(config, 'authentication_method_cookie_domain', None)
|
||||
return 'document.cookie=`loops_auth_method=${this.value}; path=/; expires=Sun, 31 Jan 2027 12:00:00 UTC%s`' % (domain and f'; domain={domain}' or '')
|
||||
|
||||
|
||||
class Unauthorized(LoginPage):
|
||||
|
||||
def __call__(self):
|
||||
response = self.request.response
|
||||
# make sure that squid does not keep the response in the cache
|
||||
response.setHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT')
|
||||
response.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate')
|
||||
response.setHeader('Pragma', 'no-cache')
|
||||
logger.warn(f'unauthorized: user={self.request.principal.id}, authMethod={self.authMethod}')
|
||||
if self.isAnonymous:
|
||||
return super(Unauthorized, self).__call__() # open or redirect to login page
|
||||
else:
|
||||
response.setStatus(403)
|
||||
return 'Unauthorized: You are not allowed to access this ressource.'
|
||||
|
||||
|
||||
def getConfigAuthMethod():
|
||||
return getattr(config, 'authentication_method', 'legacy')
|
||||
|
||||
def getAuthMethodCookieValue(request):
|
||||
default = getattr(config, 'authentication_method_cookie_default', 'legacy')
|
||||
return request.cookies.get('loops_auth_method') or default
|
||||
return request.cookies.get('loops_auth_method') or 'legacy'
|
||||
|
||||
|
||||
# OIDC authentication
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@
|
|||
<select name="auth_method"
|
||||
onchange="document.cookie=`loops_auth_method=${this.value}; path=/; expires=Sun, 31 Jan 2027 12:00:00 UTC`"
|
||||
tal:define="meth view/authMethod"
|
||||
tal:attributes="value meth;
|
||||
onchange view/authMethodCookieString">
|
||||
tal:attributes="value meth">
|
||||
<option value="legacy" i18n:translate="authentication-method-legacy"
|
||||
tal:attributes="selected python:meth=='legacy'">Legacy</option>
|
||||
<option value="oidc" i18n:translate="authentication-method-oidc"
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ license = {text = "MIT"}
|
|||
keywords = ["loops"]
|
||||
authors = [{name = "Helmut Merz", email = "helmutm@cy55.de"}]
|
||||
|
||||
# zope.publisher: v5.2.1 needed for handling of :records fields,
|
||||
# should be replaced by patched zope.publisher fork (with multipart monkey patch)
|
||||
dependencies = [
|
||||
"cybertools",
|
||||
"py-scopes",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue