authentication methods: allow selection on personal info page, set cookie, check on login page

This commit is contained in:
Helmut Merz 2025-10-25 17:31:25 +02:00
parent f9474e59db
commit d588469bb5
5 changed files with 60 additions and 5 deletions

View file

@ -39,6 +39,7 @@ from loops.organize.util import getPrincipalFolder
import loops.browser.util import loops.browser.util
from loops.util import _ from loops.util import _
import config
organize_macros = ViewPageTemplateFile('view_macros.pt') organize_macros = ViewPageTemplateFile('view_macros.pt')
@ -60,6 +61,13 @@ class PersonalInfo(ConceptView):
def view(self): def view(self):
return self return self
@Lazy
def selectAuthMethod(self):
return getattr(config, 'authentication_method', 'legacy') == 'cookie'
def getAuthMethod(self):
return self.request.cookies.get('loops_auth_method') or 'legacy'
@Lazy @Lazy
def extUserLink(self): def extUserLink(self):
from scopes.web.auth.oidc import IExternalPrincipal from scopes.web.auth.oidc import IExternalPrincipal

View file

@ -60,6 +60,20 @@
<li tal:condition="python:item.globalOptions('organize.useFilters')"> <li tal:condition="python:item.globalOptions('organize.useFilters')">
<a href="edit_filters.html" <a href="edit_filters.html"
i18n:translate="">Edit Filters</a></li> i18n:translate="">Edit Filters</a></li>
<li tal:condition="item/selectAuthMethod">
<span>Authentication Method:</span>
<select name="auth_method"
onchange="document.cookie=`loops_auth_method=${this.value}; path=/`"
tal:define="meth item/getAuthMethod"
tal:attributes="value meth">
<option value="legacy"
tal:attributes="selected python:meth=='legacy'">Legacy</option>
<option value="oidc"
tal:attributes="selected python:meth=='oidc'">OpenID Connect</option>
<option value="select"
tal:attributes="selected python:meth=='select'">Selection</option>
</select>
</li>
</ul> </ul>
</metal:block> </metal:block>
</metal:block> </metal:block>

View file

@ -5,24 +5,56 @@
from scopes.web.auth import oidc from scopes.web.auth import oidc
from zope.authentication.interfaces import IAuthentication from zope.authentication.interfaces import IAuthentication
from zope.browserpage import ViewPageTemplateFile
from zope.component import provideAdapter, getUtility, provideUtility from zope.component import provideAdapter, getUtility, provideUtility
from zope.interface import implementer, Interface from zope.interface import implementer, Interface
from zope.publisher.interfaces.browser import IBrowserRequest, IBrowserPage from zope.publisher.interfaces.browser import IBrowserRequest, IBrowserPage
from zope.publisher.browser import BrowserPage from zope.publisher.browser import BrowserPage
from zope.security.proxy import removeSecurityProxy from zope.security.proxy import removeSecurityProxy
import config
def registerAuthUtility(config): def registerAuthUtility(config):
baseAuth = getUtility(IAuthentication) baseAuth = getUtility(IAuthentication)
print('*** registerAuthUtility, baseAuth:', baseAuth) print('*** registerAuthUtility, baseAuth:', baseAuth)
provideUtility(oidc.OidcAuthentication(baseAuth)) provideUtility(oidc.OidcAuthentication(baseAuth))
class LoginView: class LoginPage:
index = ViewPageTemplateFile('loginform.pt')
def __init__(self, context, request):
self.context = context
self.request = request
self.authMethod = getattr(config, 'authentication_method', 'legacy')
if self.authMethod == 'cookie':
self.authMethod = getAuthMethodCookieValue(request)
self.oidc_allowed = self.authMethod in ('oidc', 'select')
def __call__(self): def __call__(self):
print('***', self.request.principal.id)
print('***', self.authMethod)
if self.authMethod == 'oidc':
return self.authOidc()
return self.index()
def authOidc(self):
oidc.Authenticator(self.request).login() oidc.Authenticator(self.request).login()
return '' return ''
def getAuthMethodCookieValue(request):
print('***', dict(request.cookies))
return request.cookies.get('loops_auth_method') or 'legacy'
# OIDC authentication
class LoginView(LoginPage):
def __call__(self):
return self.authOidc()
class CallbackView: class CallbackView:

View file

@ -25,7 +25,8 @@
<p i18n:translate="" tal:condition="python: principal != 'zope.anybody'"> <p i18n:translate="" tal:condition="python: principal != 'zope.anybody'">
You are not authorized to perform this action. However, you may login as a You are not authorized to perform this action. However, you may login as a
different user who is authorized.</p> different user who is authorized.</p>
<p i18n:domain="loops"> <p i18n:domain="loops"
tal:condition="view/oidc_allowed">
<a tal:attributes="href string:/@@auth_login?camefrom=$camefrom" <a tal:attributes="href string:/@@auth_login?camefrom=$camefrom"
i18n:translate="login-with-oidc">Login with OpenID Connect (Zitadel)</a> i18n:translate="login-with-oidc">Login with OpenID Connect (Zitadel)</a>
</p> </p>

View file

@ -2,9 +2,9 @@
xmlns="http://namespaces.zope.org/zope" xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"> xmlns:browser="http://namespaces.zope.org/browser">
<browser:page <browser:page for="*"
name="loginForm.html" for="*" name="loginForm.html"
template="loginform.pt" class="loops.server.auth.LoginPage"
permission="zope.Public" permission="zope.Public"
layer="cybertools.browser.loops.Loops" /> layer="cybertools.browser.loops.Loops" />