0

After disabling the default configuration of repoze.who by removing all the base_config.sa_auth... and base_config.auth_backend from config/app_.cfg.pyit should be possible to configure repoze.who as middleware in config/middleware.py.

so i created a file config/auth.py like this:

from logging import getLogger

from repoze.who.middleware import PluggableAuthenticationMiddleware
from repoze.who.classifiers import default_challenge_decider, default_request_classifier

from repoze.who.plugins.basicauth import BasicAuthPlugin
from repoze.who.plugins.htpasswd import HTPasswdPlugin, plain_check

def add_auth(app):
    htpasswd = HTPasswdPlugin('/.../htpasswd', plain_check)
    authenticators = [('htpasswd', htpasswd)]

    base_auth = BasicAuthPlugin('Inventory DB')
    challengers = [('base_auth', base_auth)]
    identifiers = [('base_auth', base_auth)]

    mdproviders = []

    log_stream = getLogger('auth')

    app_with_mw = PluggableAuthenticationMiddleware(
        app,
        identifiers,
        authenticators,
        challengers,
        mdproviders,
        default_request_classifier,
        default_challenge_decider,
        log_stream,
        )
    return app_with_mw

where plain_text passwords are used just for testing. Then, in config/middleware.py this function is imported and applied to the app as the last step in the make_app function.

from invdb.config.app_cfg import base_config
from invdb.config.environment import load_environment
from auth import add_auth

__all__ = ['make_app']
make_base_app = base_config.setup_tg_wsgi_app(load_environment)
def make_app(global_conf, full_stack=True, **app_conf):
    app = make_base_app(global_conf, full_stack=True, **app_conf)
    app = add_auth(app)
    return app

The problem is now, that the authentication does not really work. Controllers that do not require any authentication don't challenge. A controller with allow_only = tg.predicate.not_anonymous will challenge a http-authentication. But even if plain_check returns True the login is instantly forgotten and the challenge is displayed again. tg.request.identity stays None.

What am I doing wrong?

Sunday
  • 631
  • 1
  • 7
  • 14

2 Answers2

2

There is a far easier solution that replacing the whole authentication stack with a custom middleware. As mentioned by authentication documentation you can configure the authentication stack from your app_cfg.py itself.

For example if you want to force the basic auth usage you just need to set the challengers, identifiers and authenticators and then return the data to fill tg.request.identity using the authmetadata

Here is a sample app_cfg for basic auth (remember to remove any other basic_config.sa_auth entry or it might crash for unexpected parameters):

# Name our custom auth backend, if this is None TG will
# disable the whole authentication stack.
base_config.auth_backend = 'htpasswd'

from tg.configuration.auth import TGAuthMetadata

#This tells to TurboGears how to retrieve the data for your user
class ApplicationAuthMetadata(TGAuthMetadata):
    def __init__(self, sa_auth):
        self.sa_auth = sa_auth

    def get_user(self, identity, userid):
        # As we use htpasswd for authentication
        # we cannot lookup the user in a database,
        # so just return a fake user object
        from tg.util import Bunch
        return Bunch(display_name=userid, user_name=userid)

    def get_groups(self, identity, userid):
        # If the user is manager we give him the
        # managers group, otherwise no groups
        if userid == 'manager':
            return ['managers']
        else:
            return []

    def get_permissions(self, identity, userid):
        return []

base_config.sa_auth.authmetadata = ApplicationAuthMetadata(base_config.sa_auth)

from repoze.who.plugins.basicauth import BasicAuthPlugin
from repoze.who.plugins.htpasswd import HTPasswdPlugin, plain_check

# Use htpasswd for checking user credentials, remember to write the password in clear
# text as we are using the plain_check function to check them.
base_config.sa_auth.authenticators = [('htpasswd', HTPasswdPlugin('./passwd_file', plain_check))]

# Use BasicAuth plugin to ask user for credentials, this will replace
# the whole login form and cookie based authentication
base_auth = BasicAuthPlugin('MyTGApp')
base_config.sa_auth.challengers = [('basicauth', base_auth)]
base_config.sa_auth.identifiers = [('basicauth', base_auth)]

# Disable the login form, it won't work anyway as the credentials
# for basic auth must be provided through the browser itself
base_config.sa_auth.form_identifies = False
amol
  • 1,771
  • 1
  • 11
  • 15
  • thx, great solution. I have thought about creating a fake user objects and fake groups, but deemed it to complicated for this task. – Sunday May 26 '14 at 20:38
0

The answer by amol is propably how you should do it. Anyhow, here is the solution i have found in the meantime: Do not use the builtin predicate.not_anonymous. This seems to rely on parts of repoze.what or AuthMetaData which are not configured. So i invented my own predicate:

class identified_user(predicates.Predicate):
    def evaluate(self, environ, credentials):
        if environ.get('repoze.who.identity') is None:
            self.unmet()
Sunday
  • 631
  • 1
  • 7
  • 14