mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Enable mod_proxy to sit in front of TurboGears and pass along the
kerberos principal name Add an identity an visit class to TurboGears that can handle the user without requiring a database Update the UI to show the user correctly. Note that this is currently disabled. It is hardcoded to always return the principal test@FREEIPA.ORG in proxyprovider.py It doesn't handle an unauthorized request because that can never happen.
This commit is contained in:
parent
37d10e0c51
commit
182fbe3094
@ -13,8 +13,19 @@
|
||||
|
||||
# If you have sqlite, here's a simple default to get you started
|
||||
# in development
|
||||
sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite"
|
||||
# sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite"
|
||||
|
||||
# Our our sqlobject-derived proxy provider
|
||||
identity.provider='proxyprovider'
|
||||
|
||||
# the first thing checked on any request. We want to short-circuit this
|
||||
# as early as possible
|
||||
identity.source = 'visit'
|
||||
|
||||
# Turn on identity and visit (visit is required for identity)
|
||||
identity.on=True
|
||||
visit.on=True
|
||||
visit.manager='proxyvisit'
|
||||
|
||||
# if you are using a database or table type without transactions
|
||||
# (MySQL default, for example), you should turn off transactions
|
||||
|
@ -4,6 +4,7 @@ start-ipagui.py
|
||||
ipa_gui.egg-info/PKG-INFO
|
||||
ipa_gui.egg-info/SOURCES.txt
|
||||
ipa_gui.egg-info/dependency_links.txt
|
||||
ipa_gui.egg-info/entry_points.txt
|
||||
ipa_gui.egg-info/not-zip-safe
|
||||
ipa_gui.egg-info/paster_plugins.txt
|
||||
ipa_gui.egg-info/requires.txt
|
||||
@ -13,8 +14,14 @@ ipagui/__init__.py
|
||||
ipagui/controllers.py
|
||||
ipagui/json.py
|
||||
ipagui/model.py
|
||||
ipagui/proxyprovider.py
|
||||
ipagui/proxyvisit.py
|
||||
ipagui/release.py
|
||||
ipagui/config/__init__.py
|
||||
ipagui/forms/__init__.py
|
||||
ipagui/forms/user.py
|
||||
ipagui/helpers/__init__.py
|
||||
ipagui/helpers/userhelper.py
|
||||
ipagui/templates/__init__.py
|
||||
ipagui/tests/__init__.py
|
||||
ipagui/tests/test_controllers.py
|
||||
|
6
ipa-server/ipa-gui/ipa_gui.egg-info/entry_points.txt
Normal file
6
ipa-server/ipa-gui/ipa_gui.egg-info/entry_points.txt
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
[turbogears.identity.provider]
|
||||
proxyprovider = ipagui.proxyprovider:ProxyIdentityProvider
|
||||
|
||||
[turbogears.visit.manager]
|
||||
proxyvisit = ipagui.proxyvisit:ProxyVisitManager
|
@ -8,6 +8,7 @@ from turbogears import controllers, expose, flash
|
||||
from turbogears import validators, validate
|
||||
from turbogears import widgets, paginate
|
||||
from turbogears import error_handler
|
||||
from turbogears import identity
|
||||
# from model import *
|
||||
# import logging
|
||||
# log = logging.getLogger("ipagui.controllers")
|
||||
@ -27,7 +28,6 @@ user_edit_form = forms.user.UserEditForm()
|
||||
password_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
client = ipa.ipaclient.IPAClient(True)
|
||||
client.set_principal("test@FREEIPA.ORG")
|
||||
|
||||
user_fields = ['*', 'nsAccountLock']
|
||||
|
||||
@ -45,10 +45,12 @@ def utf8_encode(value):
|
||||
class Root(controllers.RootController):
|
||||
|
||||
@expose(template="ipagui.templates.welcome")
|
||||
@identity.require(identity.not_anonymous())
|
||||
def index(self):
|
||||
return dict()
|
||||
|
||||
@expose()
|
||||
@identity.require(identity.not_anonymous())
|
||||
def topsearch(self, **kw):
|
||||
if kw.get('searchtype') == "Users":
|
||||
return self.userlist(uid=kw.get('searchvalue'))
|
||||
@ -62,6 +64,7 @@ class Root(controllers.RootController):
|
||||
########
|
||||
|
||||
@expose("ipagui.templates.usernew")
|
||||
@identity.require(identity.not_anonymous())
|
||||
def usernew(self, tg_errors=None):
|
||||
"""Displays the new user form"""
|
||||
if tg_errors:
|
||||
@ -70,9 +73,11 @@ class Root(controllers.RootController):
|
||||
return dict(form=user_new_form)
|
||||
|
||||
@expose()
|
||||
@identity.require(identity.not_anonymous())
|
||||
def usercreate(self, **kw):
|
||||
"""Creates a new user"""
|
||||
restrict_post()
|
||||
client.set_principal(identity.current.user_name)
|
||||
if kw.get('submit') == 'Cancel':
|
||||
turbogears.flash("Add user cancelled")
|
||||
raise turbogears.redirect('/userlist')
|
||||
@ -104,11 +109,13 @@ class Root(controllers.RootController):
|
||||
|
||||
|
||||
@expose("ipagui.templates.useredit")
|
||||
@identity.require(identity.not_anonymous())
|
||||
def useredit(self, uid, tg_errors=None):
|
||||
"""Displays the edit user form"""
|
||||
if tg_errors:
|
||||
turbogears.flash("There was a problem with the form!")
|
||||
|
||||
client.set_principal(identity.current.user_name)
|
||||
user = client.get_user_by_uid(uid, user_fields)
|
||||
user_dict = user.toDict()
|
||||
# Edit shouldn't fill in the password field.
|
||||
@ -121,9 +128,11 @@ class Root(controllers.RootController):
|
||||
return dict(form=user_edit_form, user=user_dict)
|
||||
|
||||
@expose()
|
||||
@identity.require(identity.not_anonymous())
|
||||
def userupdate(self, **kw):
|
||||
"""Updates an existing user"""
|
||||
restrict_post()
|
||||
client.set_principal(identity.current.user_name)
|
||||
if kw.get('submit') == 'Cancel Edit':
|
||||
turbogears.flash("Edit user cancelled")
|
||||
raise turbogears.redirect('/usershow', uid=kw.get('uid'))
|
||||
@ -169,8 +178,10 @@ class Root(controllers.RootController):
|
||||
|
||||
|
||||
@expose("ipagui.templates.userlist")
|
||||
@identity.require(identity.not_anonymous())
|
||||
def userlist(self, **kw):
|
||||
"""Retrieve a list of all users and display them in one huge list"""
|
||||
client.set_principal(identity.current.user_name)
|
||||
users = None
|
||||
counter = 0
|
||||
uid = kw.get('uid')
|
||||
@ -190,8 +201,10 @@ class Root(controllers.RootController):
|
||||
|
||||
|
||||
@expose("ipagui.templates.usershow")
|
||||
@identity.require(identity.not_anonymous())
|
||||
def usershow(self, uid):
|
||||
"""Retrieve a single user for display"""
|
||||
client.set_principal(identity.current.user_name)
|
||||
try:
|
||||
user = client.get_user_by_uid(uid, user_fields)
|
||||
return dict(user=user.toDict(), fields=forms.user.UserFields())
|
||||
@ -200,10 +213,12 @@ class Root(controllers.RootController):
|
||||
raise turbogears.redirect("/")
|
||||
|
||||
@validate(form=user_new_form)
|
||||
@identity.require(identity.not_anonymous())
|
||||
def usercreatevalidate(self, tg_errors=None, **kw):
|
||||
return tg_errors, kw
|
||||
|
||||
@validate(form=user_edit_form)
|
||||
@identity.require(identity.not_anonymous())
|
||||
def userupdatevalidate(self, tg_errors=None, **kw):
|
||||
return tg_errors, kw
|
||||
|
||||
@ -222,10 +237,12 @@ class Root(controllers.RootController):
|
||||
return password
|
||||
|
||||
@expose()
|
||||
@identity.require(identity.not_anonymous())
|
||||
def suggest_uid(self, givenname, sn):
|
||||
if (len(givenname) == 0) or (len(sn) == 0):
|
||||
return ""
|
||||
|
||||
client.set_principal(identity.current.user_name)
|
||||
givenname = givenname.lower()
|
||||
sn = sn.lower()
|
||||
|
||||
@ -309,7 +326,9 @@ class Root(controllers.RootController):
|
||||
#########
|
||||
|
||||
@expose("ipagui.templates.groupindex")
|
||||
@identity.require(identity.not_anonymous())
|
||||
def groupindex(self, tg_errors=None):
|
||||
client.set_principal(identity.current.user_name)
|
||||
return dict()
|
||||
|
||||
|
||||
@ -318,5 +337,7 @@ class Root(controllers.RootController):
|
||||
############
|
||||
|
||||
@expose("ipagui.templates.resindex")
|
||||
@identity.require(identity.not_anonymous())
|
||||
def resindex(self, tg_errors=None):
|
||||
client.set_principal(identity.current.user_name)
|
||||
return dict()
|
||||
|
118
ipa-server/ipa-gui/ipagui/proxyprovider.py
Normal file
118
ipa-server/ipa-gui/ipagui/proxyprovider.py
Normal file
@ -0,0 +1,118 @@
|
||||
from turbogears.identity.soprovider import *
|
||||
from turbogears.identity.visitor import *
|
||||
import logging
|
||||
|
||||
log = logging.getLogger("turbogears.identity")
|
||||
|
||||
class IPA_User(object):
|
||||
'''
|
||||
Shell of a User definition. We don't really need much here.
|
||||
'''
|
||||
|
||||
def __init__(self, user_name):
|
||||
self.user_name = user_name
|
||||
self.display_name = user_name
|
||||
self.permissions = None
|
||||
self.groups = None
|
||||
return
|
||||
|
||||
class ProxyIdentity(object):
|
||||
def __init__(self, visit_key, user=None):
|
||||
if user:
|
||||
self._user= user
|
||||
self.visit_key= visit_key
|
||||
|
||||
def _get_user(self):
|
||||
try:
|
||||
return self._user
|
||||
except AttributeError:
|
||||
# User hasn't already been set
|
||||
return None
|
||||
user= property(_get_user)
|
||||
|
||||
def _get_user_name(self):
|
||||
if not self.user:
|
||||
return None
|
||||
return self.user.user_name
|
||||
user_name= property(_get_user_name)
|
||||
|
||||
def _get_name(self):
|
||||
if not self.user:
|
||||
return None
|
||||
return self.user.name
|
||||
user_name= property(_get_name)
|
||||
|
||||
def _get_anonymous(self):
|
||||
return not self.user
|
||||
anonymous= property(_get_anonymous)
|
||||
|
||||
def _get_permissions(self):
|
||||
try:
|
||||
return self._permissions
|
||||
except AttributeError:
|
||||
# Permissions haven't been computed yet
|
||||
return None
|
||||
permissions= property(_get_permissions)
|
||||
|
||||
def _get_groups(self):
|
||||
try:
|
||||
return self._groups
|
||||
except AttributeError:
|
||||
# Groups haven't been computed yet
|
||||
return None
|
||||
groups= property(_get_groups)
|
||||
|
||||
def logout(self):
|
||||
'''
|
||||
Remove the link between this identity and the visit.
|
||||
'''
|
||||
# Clear the current identity
|
||||
anon= ProxyObjectIdentity(None,None)
|
||||
#XXX if user is None anonymous will be true, no need to set attr.
|
||||
#anon.anonymous= True
|
||||
identity.set_current_identity( anon )
|
||||
|
||||
class ProxyIdentityProvider(SqlObjectIdentityProvider):
|
||||
'''
|
||||
IdentityProvider that uses REMOTE_USER from Apache
|
||||
'''
|
||||
def __init__(self):
|
||||
super(ProxyIdentityProvider, self).__init__()
|
||||
get = turbogears.config.get
|
||||
# We can get any config variables here
|
||||
log.info( "Proxy Identity starting" )
|
||||
|
||||
def create_provider_model(self):
|
||||
pass
|
||||
|
||||
def validate_identity(self, user_name, password, visit_key):
|
||||
user = IPA_User(user_name)
|
||||
log.debug( "validate_identity %s" % user_name)
|
||||
|
||||
return ProxyIdentity(visit_key, user)
|
||||
|
||||
def validate_password(self, user, user_name, password):
|
||||
'''Validation has already occurred in the proxy'''
|
||||
return True
|
||||
|
||||
def load_identity(self, visit_key):
|
||||
try:
|
||||
# user_name= cherrypy.request.headers['X-FORWARDED-USER']
|
||||
user_name= "test@FREEIPA.ORG"
|
||||
except KeyError:
|
||||
return None
|
||||
set_login_attempted( True )
|
||||
return self.validate_identity( user_name, None, visit_key )
|
||||
|
||||
def anonymous_identity( self ):
|
||||
'''
|
||||
This shouldn't ever happen in IPA but including it to include the
|
||||
entire identity API.
|
||||
'''
|
||||
return ProxyIdentity( None )
|
||||
|
||||
def authenticated_identity(self, user):
|
||||
'''
|
||||
Constructs Identity object for user that has no associated visit_key.
|
||||
'''
|
||||
return ProxyIdentity(None, user)
|
25
ipa-server/ipa-gui/ipagui/proxyvisit.py
Normal file
25
ipa-server/ipa-gui/ipagui/proxyvisit.py
Normal file
@ -0,0 +1,25 @@
|
||||
from turbogears.visit.api import BaseVisitManager, Visit
|
||||
from turbogears import config
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger("turbogears.visit.proxyvisit")
|
||||
|
||||
class ProxyVisitManager(BaseVisitManager):
|
||||
"""Virtually empty class just so can avoid saving this stuff in a
|
||||
database."""
|
||||
def __init__(self, timeout):
|
||||
super(ProxyVisitManager,self).__init__(timeout)
|
||||
return
|
||||
|
||||
def create_model(self):
|
||||
return
|
||||
|
||||
def new_visit_with_key(self, visit_key):
|
||||
return Visit(visit_key, True)
|
||||
|
||||
def visit_for_key(self, visit_key):
|
||||
return Visit(visit_key, False)
|
||||
|
||||
def update_queued_visits(self, queue):
|
||||
return None
|
@ -14,15 +14,6 @@
|
||||
</head>
|
||||
|
||||
<body py:match="item.tag=='{http://www.w3.org/1999/xhtml}body'" py:attrs="item.items()">
|
||||
<div py:if="tg.config('identity.on') and not defined('logging_in')" id="pageLogin">
|
||||
<span py:if="tg.identity.anonymous">
|
||||
<a href="${tg.url('/login')}">Login</a>
|
||||
</span>
|
||||
<span py:if="not tg.identity.anonymous">
|
||||
Welcome ${tg.identity.user.display_name}.
|
||||
<a href="${tg.url('/logout')}">Logout</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div id="header">
|
||||
<div id="logo">
|
||||
@ -33,7 +24,15 @@
|
||||
</div>
|
||||
<div id="headerinfo">
|
||||
<div id="login">
|
||||
Logged in as: ace
|
||||
<div py:if="tg.config('identity.on') and not defined('logging_in')" id="pageLogin">
|
||||
<span py:if="tg.identity.anonymous">
|
||||
<a href="${tg.url('/login')}">Login</a>
|
||||
</span>
|
||||
<span py:if="not tg.identity.anonymous">
|
||||
Logged in as: ${tg.identity.user.display_name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="topsearch">
|
||||
<form action="${tg.url('/topsearch')}" method="post">
|
||||
|
@ -58,5 +58,11 @@ setup(
|
||||
# 'Framework :: TurboGears :: Widgets',
|
||||
],
|
||||
test_suite = 'nose.collector',
|
||||
entry_points = """
|
||||
[turbogears.identity.provider]
|
||||
proxyprovider = ipagui.proxyprovider:ProxyIdentityProvider
|
||||
[turbogears.visit.manager]
|
||||
proxyvisit = ipagui.proxyvisit:ProxyVisitManager
|
||||
""",
|
||||
)
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# LoadModule auth_kerb_module modules/mod_auth_kerb.so
|
||||
|
||||
Alias /ipa "/usr/share/ipa/ipaserver/XMLRPC"
|
||||
# Require kerberos authentication for the entire server
|
||||
|
||||
<Directory "/usr/share/ipa/ipaserver">
|
||||
<LocationMatch />
|
||||
AuthType Kerberos
|
||||
AuthName "Kerberos Login"
|
||||
KrbMethodNegotiate on
|
||||
@ -13,6 +13,37 @@ Alias /ipa "/usr/share/ipa/ipaserver/XMLRPC"
|
||||
KrbSaveCredentials on
|
||||
Require valid-user
|
||||
ErrorDocument 401 /errors/unauthorized.html
|
||||
</LocationMatch>
|
||||
|
||||
ProxyRequests Off
|
||||
|
||||
<Proxy *>
|
||||
RewriteEngine on
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
|
||||
# We create a subrequest to find REMOTE_USER. Don't do this for every
|
||||
# subrequest too (slow and huge logs result)
|
||||
RewriteCond %{IS_SUBREQ}% false
|
||||
RewriteRule .* - [E=RU:%{LA-U:REMOTE_USER}]
|
||||
RequestHeader set X-Forwarded-User %{RU}e
|
||||
|
||||
# RequestHeader unset Authorization
|
||||
</Proxy>
|
||||
|
||||
# The URI's with a trailing ! are those that aren't handled by the proxy
|
||||
ProxyPass /errors/ !
|
||||
ProxyPass /ipa !
|
||||
ProxyPass / http://localhost:8080/
|
||||
ProxyPassReverse /errors !
|
||||
ProxyPassReverse /ipa !
|
||||
ProxyPassReverse / http://localhost:8080/
|
||||
|
||||
# Configure the XML-RPC service
|
||||
|
||||
Alias /ipa "/usr/share/ipa/ipaserver/XMLRPC"
|
||||
|
||||
<Directory "/usr/share/ipa/ipaserver">
|
||||
|
||||
SetHandler mod_python
|
||||
PythonHandler ipaxmlrpc
|
||||
@ -22,3 +53,4 @@ Alias /ipa "/usr/share/ipa/ipaserver/XMLRPC"
|
||||
# this is pointless to use since it would just reload ipaxmlrpc.py
|
||||
PythonAutoReload Off
|
||||
</Directory>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user