allow usage of OIDC authentication (via py-scopes) where appropriate, and provide corresponding views in loops/server/auth.zcml
This commit is contained in:
parent
80c83d5c9f
commit
1d264fc54f
9 changed files with 65 additions and 21 deletions
|
@ -24,6 +24,8 @@
|
||||||
<include package="cyberapps.ccmkg" />
|
<include package="cyberapps.ccmkg" />
|
||||||
<include package="cyberapps.knowledge" />-->
|
<include package="cyberapps.knowledge" />-->
|
||||||
|
|
||||||
|
<include package="loops.server" file="auth.zcml" />
|
||||||
|
|
||||||
<!-- Override registrations -->
|
<!-- Override registrations -->
|
||||||
<includeOverrides package="loops" file="overrides.zcml" />
|
<includeOverrides package="loops" file="overrides.zcml" />
|
||||||
<includeOverrides file="overrides.zcml" />
|
<includeOverrides file="overrides.zcml" />
|
||||||
|
|
|
@ -9,6 +9,7 @@ server_id = getenv('SERVER_ID')
|
||||||
zope_conf = getenv('ZOPE_CONF', 'zope.conf')
|
zope_conf = getenv('ZOPE_CONF', 'zope.conf')
|
||||||
server_port = getenv('SERVER_PORT',
|
server_port = getenv('SERVER_PORT',
|
||||||
server_id and getenv(f'SERVER_PORT_{server_id}')) or '8080'
|
server_id and getenv(f'SERVER_PORT_{server_id}')) or '8080'
|
||||||
|
base_url = getenv('BASE_URL', 'https://test.example.com')
|
||||||
|
|
||||||
shell_pw = (getenv('SHELL_PW', 'dummy'))
|
shell_pw = (getenv('SHELL_PW', 'dummy'))
|
||||||
loops_path = (getenv('LOOPS_PATH', 'loops/demo'))
|
loops_path = (getenv('LOOPS_PATH', 'loops/demo'))
|
||||||
|
@ -20,3 +21,20 @@ dbname = getenv('DBNAME', 'demo')
|
||||||
dbuser = getenv('DBUSER', 'demo')
|
dbuser = getenv('DBUSER', 'demo')
|
||||||
dbpassword = getenv('DBPASSWORD', 'secret')
|
dbpassword = getenv('DBPASSWORD', 'secret')
|
||||||
dbschema = getenv('DBSCHEMA', 'demo')
|
dbschema = getenv('DBSCHEMA', 'demo')
|
||||||
|
|
||||||
|
# OpenID Connect (OIDC, e.g. via zitadel) authentication settings
|
||||||
|
oidc_provider = getenv('OIDC_PROVIDER', '') #'https://instance1-abcdef.zitadel.cloud')
|
||||||
|
oidc_client_id = getenv('OIDC_CLIENT_ID', '12345')
|
||||||
|
oidc_params = dict(
|
||||||
|
op_config_url=oidc_provider + '/.well-known/openid-configuration',
|
||||||
|
op_uris=None,
|
||||||
|
op_keys=None,
|
||||||
|
callback_url=getenv('OIDC_CALLBACK_URL', base_url + '/auth_callback'),
|
||||||
|
client_id=oidc_client_id,
|
||||||
|
principal_prefix=getenv('OIDC_PRINCIPAL_PREFIX', 'loops.'),
|
||||||
|
cookie_name=getenv('OIDC_COOKIE_NAME', 'oidc_' + oidc_client_id),
|
||||||
|
cookie_domain=getenv('OIDC_COOKIE_DOMAIN', None),
|
||||||
|
cookie_lifetime=getenv('OIDC_COOKIE_LIFETIME', '86400'),
|
||||||
|
cookie_crypt=getenv('OIDC_COOKIE_CRYPT', None)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ from loops.interfaces import HtmlText
|
||||||
from loops.organize.util import getPrincipalFolder, getPrincipalForUserId
|
from loops.organize.util import getPrincipalFolder, getPrincipalForUserId
|
||||||
from loops import util
|
from loops import util
|
||||||
from loops.util import _
|
from loops.util import _
|
||||||
from scopes.web.auth import oidc
|
|
||||||
|
|
||||||
ANNOTATION_KEY = 'loops.organize.person'
|
ANNOTATION_KEY = 'loops.organize.person'
|
||||||
|
|
||||||
|
@ -34,7 +33,7 @@ def raiseValidationError(info):
|
||||||
|
|
||||||
|
|
||||||
class UserId(schema.TextLine):
|
class UserId(schema.TextLine):
|
||||||
""" Obsolete, as member registration does not use zope.formlib any more.
|
""" Note: member registration does not use zope.formlib any more.
|
||||||
TODO: transfer validation to loops.organize.browser.
|
TODO: transfer validation to loops.organize.browser.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -44,11 +43,6 @@ class UserId(schema.TextLine):
|
||||||
from loops.organize.party import getPersonForUser
|
from loops.organize.party import getPersonForUser
|
||||||
context = removeSecurityProxy(self.context).context
|
context = removeSecurityProxy(self.context).context
|
||||||
principal = getPrincipalForUserId(userId, context)
|
principal = getPrincipalForUserId(userId, context)
|
||||||
#auth = component.getUtility(IAuthentication, context=context)
|
|
||||||
#try:
|
|
||||||
#principal = auth.getPrincipal(userId)
|
|
||||||
#except PrincipalLookupError:
|
|
||||||
#principal = oidc.Principal(userId, dict(name=userId))
|
|
||||||
if principal is None:
|
if principal is None:
|
||||||
raiseValidationError(_('User $userId does not exist',
|
raiseValidationError(_('User $userId does not exist',
|
||||||
mapping={'userId': userId}))
|
mapping={'userId': userId}))
|
||||||
|
|
|
@ -33,7 +33,6 @@ from loops.security.common import getCurrentPrincipal
|
||||||
from loops.security.interfaces import ISecuritySetter
|
from loops.security.interfaces import ISecuritySetter
|
||||||
from loops.type import TypeInterfaceSourceList
|
from loops.type import TypeInterfaceSourceList
|
||||||
from loops import util
|
from loops import util
|
||||||
from scopes.web.auth import oidc
|
|
||||||
|
|
||||||
|
|
||||||
# register type interfaces - (TODO: use a function for this)
|
# register type interfaces - (TODO: use a function for this)
|
||||||
|
@ -87,7 +86,6 @@ class Person(AdapterBase, BasePerson):
|
||||||
setter = ISecuritySetter(self)
|
setter = ISecuritySetter(self)
|
||||||
if userId:
|
if userId:
|
||||||
principal = self.getPrincipalForUserId(userId)
|
principal = self.getPrincipalForUserId(userId)
|
||||||
print('***', userId, principal)
|
|
||||||
if principal is None:
|
if principal is None:
|
||||||
return
|
return
|
||||||
person = getPersonForUser(self.context, principal=principal)
|
person = getPersonForUser(self.context, principal=principal)
|
||||||
|
@ -144,14 +142,6 @@ class Person(AdapterBase, BasePerson):
|
||||||
def getPrincipalForUserId(self, userId=None):
|
def getPrincipalForUserId(self, userId=None):
|
||||||
userId = userId or self.userId
|
userId = userId or self.userId
|
||||||
return getPrincipalForUserId(userId, self.context, self.authentication)
|
return getPrincipalForUserId(userId, self.context, self.authentication)
|
||||||
if not userId:
|
|
||||||
return None
|
|
||||||
auth = self.authentication
|
|
||||||
try:
|
|
||||||
return auth.getPrincipal(userId)
|
|
||||||
except PrincipalLookupError:
|
|
||||||
return oidc.Principal(userId, dict(name=userId))
|
|
||||||
#return None
|
|
||||||
|
|
||||||
|
|
||||||
def getAuthenticationUtility(context):
|
def getAuthenticationUtility(context):
|
||||||
|
|
|
@ -15,7 +15,6 @@ from zope.traversing.api import getParents
|
||||||
from loops.common import adapted
|
from loops.common import adapted
|
||||||
from loops.security.common import getCurrentPrincipal
|
from loops.security.common import getCurrentPrincipal
|
||||||
from loops.type import getOptionsDict
|
from loops.type import getOptionsDict
|
||||||
from scopes.web.auth import oidc
|
|
||||||
|
|
||||||
defaultAuthPluginId = 'loops'
|
defaultAuthPluginId = 'loops'
|
||||||
|
|
||||||
|
@ -93,6 +92,7 @@ def getPrincipalForUserId(id, context=None, auth=None):
|
||||||
try:
|
try:
|
||||||
return auth.getPrincipal(id)
|
return auth.getPrincipal(id)
|
||||||
except PrincipalLookupError:
|
except PrincipalLookupError:
|
||||||
|
from scopes.web.auth import oidc
|
||||||
return oidc.Principal(id, dict(name=id))
|
return oidc.Principal(id, dict(name=id))
|
||||||
#return None
|
#return None
|
||||||
|
|
||||||
|
|
18
loops/server/auth.zcml
Normal file
18
loops/server/auth.zcml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<configure
|
||||||
|
xmlns="http://namespaces.zope.org/zope"
|
||||||
|
xmlns:browser="http://namespaces.zope.org/browser">
|
||||||
|
|
||||||
|
<browser:page
|
||||||
|
for="zope.interface.Interface"
|
||||||
|
name="auth_login"
|
||||||
|
class="loops.server.auth.LoginView"
|
||||||
|
permission="zope.Public" />
|
||||||
|
|
||||||
|
<browser:page
|
||||||
|
for="zope.interface.Interface"
|
||||||
|
name="auth_callback"
|
||||||
|
class="loops.server.auth.CallbackView"
|
||||||
|
permission="zope.Public" />
|
||||||
|
|
||||||
|
</configure>
|
||||||
|
|
|
@ -15,7 +15,8 @@ import waitress
|
||||||
from zope.app.wsgi import config, getWSGIApplication
|
from zope.app.wsgi import config, getWSGIApplication
|
||||||
|
|
||||||
def run(app, config):
|
def run(app, config):
|
||||||
oidc.startup()
|
if config.oidc_provider:
|
||||||
|
oidc.startup()
|
||||||
port = int(config.server_port)
|
port = int(config.server_port)
|
||||||
print(f'Serving on port {port}.')
|
print(f'Serving on port {port}.')
|
||||||
waitress.serve(app, port=port)
|
waitress.serve(app, port=port)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# py-scopes/demo/config.py
|
# loops/tests/config.py
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from scopes.server.app import zope_app_factory
|
from scopes.web.app import zope_app_factory
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
@ -18,3 +18,21 @@ dbuser = getenv('DBUSER', 'demo')
|
||||||
dbpassword = getenv('DBPASSWORD', 'secret')
|
dbpassword = getenv('DBPASSWORD', 'secret')
|
||||||
dbschema = getenv('DBSCHEMA', 'demo')
|
dbschema = getenv('DBSCHEMA', 'demo')
|
||||||
|
|
||||||
|
base_url = 'test://'
|
||||||
|
|
||||||
|
# authentication settings
|
||||||
|
oidc_provider = ''
|
||||||
|
oidc_client_id = getenv('OIDC_CLIENT_ID', '12345')
|
||||||
|
oidc_params = dict(
|
||||||
|
op_config_url=oidc_provider + '/.well-known/openid-configuration',
|
||||||
|
op_uris=None,
|
||||||
|
op_keys=None,
|
||||||
|
callback_url=getenv('OIDC_CALLBACK_URL', base_url + '/auth/callback'),
|
||||||
|
client_id=oidc_client_id,
|
||||||
|
principal_prefix=getenv('OIDC_PRINCIPAL_PREFIX', 'loops.'),
|
||||||
|
cookie_name=getenv('OIDC_COOKIE_NAME', 'oidc_' + oidc_client_id),
|
||||||
|
cookie_domain=getenv('OIDC_COOKIE_DOMAIN', None),
|
||||||
|
cookie_lifetime=getenv('OIDC_COOKIE_LIFETIME', '86400'),
|
||||||
|
cookie_crypt=getenv('OIDC_COOKIE_CRYPT', None)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
# loops.tests.test_loops
|
# loops.tests.test_loops
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
sys.path = [os.path.dirname(__file__)] + sys.path
|
||||||
|
|
||||||
import unittest, doctest
|
import unittest, doctest
|
||||||
import warnings
|
import warnings
|
||||||
from zope.interface.verify import verifyClass
|
from zope.interface.verify import verifyClass
|
||||||
|
|
Loading…
Add table
Reference in a new issue