Tags:
create new tag
view all tags
This is the third article in a series to describe how I am going to fit TWiki's DakarRelease into an intranet portal. See HowToAvoidRegistration and TransparentAuthenticationRevisited for more explanations. I also count Dakar's Item 594 as related, though I'm afraid that I'm only adding to the confusion...

Registration On Demand

This is only useful if you always have $ENV{REMOTE_USER} set, for example because the whole TWiki directory - or the whole server - require users to authenticate before they even view a single page.

This is a hack and not (yet) a contrib. A proof of concept for HowToAvoidRegistration. I've called it RegistrationOnDemandHack, because it actually doesn't avoid registration at all - it just postpones it until I think that TWiki ought to ask for it.

Nevertheless I am likely to deploy it in my own TWiki, so I'd be very grateful if further Dakar releases would allow the patch to be applied without too much changes before I (or someone else) comes up with something which could be called a Contrib. ;->

The basic idea is, as outlined in HowToAvoidRegistration, to separate between the "authentication" and "authorization". The hack doesn't require any significant code changes in the TWiki core, just some additions, and it abuses unpublished interfaces.

Design outline

The 'authenticated' context in TWiki's session object is silently being interpreted as ' registered and authenticated' (or authorized and authenticated, to bring it to the terminology of Anton Aylward). In a TWiki driven by TemplateLogin, this is the normal flow: You need to register first to get a user id suitable for authentication. But usually, authentication comes first in every session. Only after you have verified the client's identity he is granted appropriate access rights.

Given an external authentication managenent, Twiki draws the wrong conclusion about the context. It calls the session 'authenticated' just because it finds $ENV{REMOTE_USER}, regardless of whether the user ever registered. But we still need a registration to achieve the same TWiki status as with TemplateLogin.

Therefore I have prepared a login manager which makes sure that TWiki's 'authenticated' context is entered only when an authenticated user has registered himself before as well.

This login manager does two things:

  1. If, during client session setup, we find that the external user given $ENV{REMOTE_USER} is unknown in the TWikiUsers topic, then pretend that this is a login from 'guest' (or whatever is configured as $cfg{DefaultUserLogin}).
  2. If TWiki encounters an action which needs authentication (triggered by UI.pm to catch the TWiki::AccessControlException), our forceAuthentication routine knows that we have an authentication, but lack the TWikiRegistration. So we are just doing that on-the-fly.

The second step is similar to TemplateLogin in that it requests the data needed on-the-fly, and after successful operation returns to whatever action had been interrupted. We can even re-use it's bin/login stub to call us back without change. On the other hand, the actions performed by the second step are those of the usual TWikiRegistration process: It creates a user's home page and adds him to the TWikiUsers list.

The new logon Manager lib/TWiki/Client/RegistrationOnDemandLogin.pm

I've derived the code for this login manager from the existing managers, e.g. lib/TWiki/Client/TemplateLogin.pm.

The calling structure is:

  1. bin/* : A TWiki request calls TWiki::UI::run
    1. TWiki/UI.pm: A new session object is created there using TWiki::new
      1. TWiki.pm: After a bunch of initializations which I am gladly skipping here, the variable $login is established by loading a client session:
        my $login = $this->{client}->loadSession();
        1. TWiki/Client.pm: The login user is being evaluated:
          # See whether the user was logged in (first webserver, then
          =             =# session, then default)
          =             =my $authUser = $this->getUser( $this );
          =             The routine =getUser
          is supposed to be provided by the login manager. (I am skipping a couple of ||= statements)
          # Save the users information again if they do not appear to be a guest
          my $sessionIsAuthenticated =
          ( $authUser && $authUser ne $TWiki::cfg{DefaultUserLogin} );
          Ahaaaa! So all we need to do is to make sure that $authUser is returned as $TWiki::cfg{DefaultUserLogin} by my login manager to make the session unauthenticated.
      2. UI.pm checks whether the action is permitted, and if it isn't and the session is considered 'unauthenticated' then the login manager's forceAuthentication method is called in the block catching the AccessControlException.
        1. There we just print out a registration form, recording the original URL.
        2. The user fills the form. This is an unauthenticated procedure, so I'll skip the details. We are directly dropping into the login mangers login procedure.
        3. This routine does an on-the-fly registration and then redirects to the original url. All is starting over again, but...
      3. If checkAccess is still catching an AccessControlException,
        1. forceAuthentication returns failure (undef) because now the session is considered 'authenticated'.

How to activate

You shouldn't. You really shouldn't. It isn't tested sufficiently, it is just my personal proof of concept. But maybe you'd like to have a look at the code? This is waaaay less dangerous.

  1. Unzip the attachment into your TWiki source tree
  2. Either manually add $TWiki::cfg{LoginManager} = 'TWiki::Client::RegistrationOnDemandLogin'; to your lib/LocalSite.cfg or apply the one-line patch to lib/TWiki.cfg and activate RegistrationOnDemandLogin with bin/configure

Todo

  • Create a test suite
  • Fix bugs and documentation
  • Refactor the common code parts from =lib/TWiki/UI/Register.pm. I am abusing no less than four unpublished interfaces:
    • _getDataFromQuery( $query );
    • _validateRegistration ( $twiki, $data, $query, $topic );
    • _newUserFromTemplate($twiki, 'NewUserTemplate', $data);
    • _emailRegistrationConfirmations( $twiki, $data );
  • Refactor the common template parts from TWikiRegistration and templates/registerform.tmpl
    • Keep an eye on Item 594 on the Dakar bugs list.
  • Allow the form fields in templates/registerform.tmpl to be pre-loaded (e.g. from LDAP data)

-- HaraldJoerg - 09 Oct 2005


I agree, it is entirely silly that TWiki assumes my login name is my REMOTE_USER name.. My entire site is Apache Authentication protected, but my twiki is to be separately protected using sessions.

-- PeterPayne - 14 Feb 2006

Actually, all I had to do was comment out the line:

    $authUser ||= $twiki->{remoteUser};
in Client.pm (sub loadSession).

Now it always asks for my login even though I've got Apache Basic authentication on my site.

-- PeterPayne - 14 Feb 2006

If I understand that correctly, then your TWiki needs to do its own user management, in addition to what's in place on your site. Doesn't that mean that TWiki users have to login twice: Once to use the site (their browser will ask for that), and once to use TWiki? Do you have different persons using the same user id for Apache authentication but want to differentiate between them in TWiki? If so, then your solution seems to be a reasonable fix - for what I'd call a broken user management at the site level. Wait - additionally there's the benefit that your TWiki users can "log out", which is impossible with Apache's basic authentication without closing the browser.

What I have in mind (and what I'm using at my site) is to use the sitewide apache authentication for TWiki's user management, by automagically registering the users. This allows my TWiki, and my TWiki users, to get rid of an own password management. But of course this needs an 1:1 relationship between TWiki users and $ENV{REMOTE_USER}.

-- HaraldJoerg - 14 Feb 2006

Bug: If a user registers with the same Wiki name as someone else, then the Hack overwrites the old entry in the User List. Therefore, if there are two Joe Smiths -- one whose LDAP username is jsmith and the other whose LDAP username is jpsmith -- and jsmith is already registered, if then jpsmith later registers but forgets that there is another Joe Smith and therefore does not use his middle initial but sets his Wiki name as JoeSmith, the first Joe Smith loses his registration. The default TWiki Registration script, however, forbids registering with a Wiki name that is already in use.

-- AndrewBanks - 07 Jun 2006

Hmmmm; let's make one thing perfectly clear. This is not a hack. Deriving a new login manager is recommended practice, and I am delighted to see that someone has taken up the challenge! if you need help packaging it as a contrib (which somehow i doubt!) then just shout.

-- CrawfordCurrie - 07 Jun 2006

Crawford - it depends on what qualifies something as a hack. A diff file required for installation may be ok, but using a couple of private routines in core modules is not. I have been surprised when Andrew told me per mail that the attached file (from October 2005, that's Dakar beta phase) would still "work" in 4.0.2ish, only to find out that it definitely will fail in 4.0.3. One of the private routines in lib/TWiki/UI/Register.pm which I've been using has vanished.

So the "contrib" has survived the beta testing phase, one or two Dakar releases, to be killed by the upcoming bugfix release. Tough luck.

The user registration in TWiki4 is still carrying the "under construction" sign with it. A svn diff for Register.pm between 4.0.2 and svn HEAD has 648 lines, after 100 lines between 4.0.1 and 4.0.2. I don't dare to have another shot at either of RegistrationOnDemandHack or ApprovingRegistrations before this calms down.

At least some of the changes would IMHO have been better postponed to a 4.1 release, with a previous collection and discussion of requirements. But RegistrationBeyondDakar is still as pristine as it was in November last year, and feedback to RegistrationAsPluginRequirements wasn't overwhelming....

-- HaraldJoerg - 07 Jun 2006

Of course. The login manager API is reasonably well defined, and I naively assumed you had stayed within the bounds of that API. I was frankly a bit surprised you had been able to; now I realise you weren't frown

Yes, registration has fluxed too much. It's a shame that the registration changes were in place around the middle of last year, but it has taken this long to get the test feedback. Moving too quickly to pluggable registration fills me with fear, because we will go through a similar requirements cycle.

BTW a better way to do registration on the fly is to call TWiki::UI::register_cgi, as is done by the tests. For example:

sub registerUser {
    my ($login, $fn, $sn, $email ) = @_;

    my $query = new CGI ({
                          'TopicName' => [  'TWikiRegistration'  ],
                          'Twk1Email' => [ $email  ],
                          'Twk1WikiName' => [ $fn.$sn ],
                          'Twk1Name' => [ $fn.' '.$sn ],
                          'Twk0Comment' => [  '' ],
                          'Twk1LoginName' => [ $login ],
                          'Twk1FirstName' => [ $fn ],
                          'Twk1LastName' => [ $sn ],
                          'action' => [ 'register' ]
                         });

    my $regoff = $TWiki::cfg{Register}{NeedVerification};
    $TWiki::cfg{Register}{NeedVerification} = 0;
    $query->path_info( "/$TWiki::cfg{UsersWebName}/TWikiRegistration" );
    my $session = new TWiki( $TWiki::cfg{DefaultUserName}, $query);

    try {
        TWiki::UI::Register::register_cgi($session);
    } catch TWiki::OopsException with {
        # This should be the response on successful registration. ignore it
    } catch Error::Simple with {
         # Do what you like with other errors
    } always {
        $TWiki::cfg{Register}{NeedVerification} = $regoff;
    }
}
This code should work on 4.0.0 through 4.0.3.

-- CrawfordCurrie - 08 Jun 2006

Crawford: Yes, this would have been a sensible approach - only that it breaks a function which I had desired, and which, as it seems, is the reason behind the bug found by Andrew.

I had desired to make registration "on-the-fly" so that, after successful registration, the user is redirected back to wherever he had been before he was asked to register (there's an extra hidden parameter origurl for that, which is missing in Register.pm). The conventional registration process ends in an oops page which is somewhat misleading in this context.

Unfortunately _validateRegistration in Register.pm checks for the existence of the user topic in the web passed by the form (as PATH_INFO ) instead of the user web - so my hack fails if the user has been in a web different from Main when being asked to register.

I'd guess the following one-line-patch would fix the bug:

$ diff -U2 registerform.tmpl~ registerform.tmpl
--- registerform.tmpl~  2005-10-10 00:40:58.000000000 +0200
+++ registerform.tmpl   2006-06-08 23:56:22.000000000 +0200
@@ -16,5 +16,5 @@
 %TMPL:P{"simpleheader"}%
 ---+ %BANNER%
-<form action='%SCRIPTURL%/login%SCRIPTSUFFIX%/%WEB%/%TOPIC%' method='post'>
+<form action='%SCRIPTURL%/login%SCRIPTSUFFIX%/%MAINWEB%/%HOMETOPIC%' method='post'>
 <table border='0'>
   <td align="right"> First Name: </td>

After successful registration a user now ends up in Main.WebHome, which may be confusing - but since 4.0.3 is ante portas, I can't convince myself for a better solution.

One last comment on Crawford's "Moving too quickly to pluggable registration": I never wanted to move quickly. That's why I never pressed to include any of my registration changes into Dakar, and instead wrote some topics about collecting the requirements for Edinburgh. Registration should be done with due care, given the difference between intranets (LDAP available, external user management) and internets, where the first priority is fighting SPAM.

-- HaraldJoerg - 08 Jun 2006

Argh. There are so many topics floating around on this I just don't know where to go. Makes me wish there were a mailing list :-).

I have a similar need to many here: I have an intranet TWiki installation where authentication is done via a proprietary Apache plugin that sets REMOTE_USER to the user's corporate ID (a number) before even the first page of the Wiki is ever displayed. So, users are always authenticated and can never "log out". The plugin manages special cookies that expire after so many hours etc. etc. TWiki should not need to care about any of that stuff.

We don't want people to be known on the site by their corporate ID, of course, nor do we want to have to use that for authorization, so we still asked people to register and create a WikiName, which was added to TWikiUsers and their own personal Wiki page was created. We have a corporate LDAP database which I queried (using the corporate ID from REMOTE_USER to obtain things like the email address, etc.)

In Cairo, I had hacked up the register script to do all the above and it was working quite well.

In Dakar, I'm pretty lost unfortunately. This is what I would like:

  • Authentication is assumed if REMOTE_USER is set (which it always will be for me).
  • Mapping is done between the login value (the corporate ID from REMOTE_USER) and the Wiki name--I presume taken from TWikiUsers.

I seem to have the above working, more or less. If I have an entry in TWikiUsers then it's used, otherwise the corporate ID is used. The rest of it eludes me however:

  • I don't want any "Log In" options to be displayed in the menu bar.
  • I only want a "Register" option to be displayed if the user hasn't already registered (doesn't have any entry in TWikiUsers yet)
  • I'm OK with having the user be forced to click a link to register: the "automatically register" capability would be nice but it's not that important to me.
  • In the registration screen, I don't want to display the login name at all.
  • I don't want to ask for or set any sort of password during registration.
  • I don't want to do the verification email thing.
  • I want to obtain the email address from LDAP. In my Cairo implementation I put this in a hidden field so the user didn't even have a chance to change it; this would be nice but is not essential.
  • There are other pieces of information such as phone number, etc. that I was obtaining from LDAP and putting into the user's new Wiki page: this is nice but optional.

I've done a lot of Perl programming but I don't know TWiki's internals, esp. Dakar. I've created myself a new TWiki::Client::FooLogin class which is currently just a copy of the ApacheLogin class. I've looked at the code in the RegistrationOnDemandLogin class, but I'm scared off because it apparently doesn't work in TWiki 4.0.3 and above (I'm running TWiki 4.0.4 with hotfix 2).

In configure I have UseClientSessions off, MapUserToWikiName on, PasswordManager none, NeedVerification off, and AllowLoginName off (according to the docs this seems like the wrong value, but it does work).

Does anyone have any hints, or pointers to documentation I've missed (I've read the pages I've found on the TWiki site, including InterfacingToExternalAccessControlListManagers, CairoDakarRegistrationDifference, TransparentAuthenticationRevisited, TWikiUserAuthentication of course, and the LoginNameAliasesPlugin.

I'm happy to share my new Client module, if I can get it working...

-- PaulSmith - 31 Jul 2006

Paul,

On our internal TWiki we are using a NTLM based apache authentication, which results in $ENV{REMOTE_USER} being set. I re-worked the code here to do just about everything you've asked for. It auto-magically fills out the needed forms from the information in the authorization cookie (our mechanism, rather than LDAP). The user is not given the option to change anything, they merely click on Register.

I am working on getting this functioning in version 4.0.4 (some Register.pm calls changed) (I have it working in 4.0.1). I will give more of an update, once I get it fully working (early next week).

-- CraigMeyer - 14 Oct 2006

Ok,

I've gotten this working in 4.0.4. I used Register::_registerSingleBulkUser() to do the work from Client::RegistrationOnDemandLogin(). registerSingle needed some work to get everything done in the proper order (correct in Register::finish()). Now, I think I should have just worked with finish() wink It uses a template register.pattern.tmpl.

Paul,

Would you put the LDAP lookups in the PERL code, or use the LDAP plugin?

-- CraigMeyer - 16 Oct 2006

I have posted a tar-ball which contains the following files:

  • Updated lib/TWiki/UI/Register.pm
  • The Diffs for lib/TWiki/UI/Register_pm.diffs
  • lib/TWiki/Client/RegistrationOnDemandLogin.pm
  • templates/register.pattern.tmpl

You should unpack this in it's OWN directory, and then copy over the files. Warning: It will overwrite whatever files are in your twiki tree!

I made modifications to TWiki::UI::Register.pm in order to make _registerSingleBulkUser() work properly.

In lib/LocalSite.cfg, I set the LoginManager to be:

$TWiki::cfg{LoginManager} = 'TWiki::Client::RegistrationOnDemandLogin';

I know this is a bit sketchy, If you have questions/problems please let me know.

-- CraigMeyer - 14 Nov 2006

In lib/TWiki/Client/RegistrationOnDemandLogin.pm, sub _register_on_demand(), The code depends on the PERL environment variable $ENV{REMOTE_USER} to be setup, by the .htaccess apache authentication. Our corporate intranet authentication also sets up two other environment variables BixToken_name (User's fullname) and BixToken_mail (User's internal email). For you to re-use this code, these need to be changed to get the values from your authentication (or LDAP server).

-- CraigMeyer - 14 Nov 2006

Hi, I've tried the hack but even if I'm already registered with a valid username/wikiname my requeste has been forwarded to the registration form in anycase. Do you kindly have any suggestion?

-- AntenoreGatta - 12 Jan 2007

Check TWikiUsers and make sure the fields: Wikiname AND REMOTE_USER are both listed and correct.

Make sure your browser is accepting cookies.

Make sure the apache .htaccess Authentication is working.

-- CraigMeyer - 15 Jan 2007

If you need support please ask questions in the Support web.

-- PeterThoeny - 16 Jan 2007

Just got this working again in V4.1 and I still needed to make modifications to UI/Register.pm _registerSingleBulkUser(). I may be doing this incorrectly wink I needed to re-order the code, so that _createUserTopic() was called, before the findUser(). And needed to get the TwikiRegistrationAgent in order to successfully add the user with addUserToMapping(), otherwise didn't have the privilege.

-- CraigMeyer - 25 Jan 2007

Topic attachments
I Attachment History Action Size Date Who Comment
Unknown file formatgz RegistrationOnDemandHack.tar.gz r2 r1 manage 4.5 K 2005-10-10 - 09:04 HaraldJoerg Registration on demand - login manager and template
Unknown file formatdiff RegistrationOnDemandLogin.diff r1 manage 0.5 K 2005-10-09 - 23:14 HaraldJoerg Allow the login manager to be /bin/configure'd
Compressed Zip archivetgz RegistrationOnDemandLogin.tgz r1 manage 17.3 K 2006-11-14 - 21:26 UnknownUser Changes for Version 4.0.4
Edit | Attach | Watch | Print version | History: r17 < r16 < r15 < r14 < r13 | Backlinks | Raw View | Raw edit | More topic actions
Topic revision: r17 - 2007-01-25 - CraigMeyer
 
  • Learn about TWiki  
  • Download TWiki
This site is powered by the TWiki collaboration platform Powered by Perl Hosted by OICcam.com Ideas, requests, problems regarding TWiki? Send feedback. Ask community in the support forum.
Copyright © 1999-2026 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.