Adding users to SharePoint dynamically at first request after authentication

That title says it all! Well I was working on case where SharePoint was configured to use external authentication. This external authentication was hosted outside SharePoint and it could be used by many other services too. End users of SharePoint would notice redirection to this external service when they click Login in the UI. And after succesful login they would be redirected back to SharePoint. All of that stuff would be done in secure way of course. From SharePoint configuration point of view this external authentication was just authentication provider. So it was configured just like any other provider (like LDAP, AD etc.). But challenge in this case was the amount of potential users for this SharePoint application... which is millions of users... and none of them cannot be known in advance! There just isn't way to know who is using the service before the user is actually using it. So the result could be only what I've described in the title... adding users dynamically to the SharePoint at first request after authentication :-)

Since this case was proof of concept we just wanted to test this functionality in action before the real project would begin. So of course my first solution was quick (and dirty) hack for this one. I just edited AccessDenied.aspx page to add user to SharePoint if it wasn't there already. And of course redirected the users back to the page before the Access Denied. This was done with notepad in working environment pretty fast. But of course that was just way to see if that kind of "Adding users dynamically" would even work (Note: Don't ever fiddle around with SharePoint aspx files like I did. I just did quick test and verified my theory before doing real solution). And after that I started to make the actually solution... and this time with real programming environment :-). 

So my solution was new HttpModule that would do all the magic. Downside of this approach is of course that this code is run on every request. (Unlike the super hack on AccessDenied.aspx :-). So it needs to be as light as it only can be. The code itself is pretty easy and straight forward... so let's take a look at it:

 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
 using System;using System.Collections.Generic;using System.Text;using System.Web;using Microsoft.SharePoint;using System.Security.Principal;using Microsoft.SharePoint.WebControls;using System.Configuration;namespace Microsoft.MCS.Authorization{  public class AddUserToSystem : IHttpModule  {    private void AddUser()    {      if (SPContext.Current.Web.CurrentUser == null)      {        // This user isn't SharePoint User => Let's add it         SPSecurity.RunWithElevatedPrivileges(delegate()        {          using (SPSite site = new SPSite(SPControl.GetContextSite(HttpContext.Current).ID))          {            using (SPWeb web = site.OpenWeb("/"))            {              Boolean allowUnsafeUpdate = web.AllowUnsafeUpdates;              try              {                web.AllowUnsafeUpdates = true;                // Get user identity name                String login = HttpContext.Current.User.Identity.Name;                String loginName = "ext:" + login;                String name = login.Substring(3, login.IndexOf(',') - 3);                String email = "";                String notes = "Automatically added user";                // Or take from ConfigurationManager.AppSettings[...];                String groupName = "Visitors";                web.SiteUsers.Add(loginName, email, name, notes);                web.Groups[groupName].AddUser(loginName, email, name, notes);                HttpContext.Current.Response.Redirect(HttpContext.Current.Request.Url.ToString());              }              finally              {                web.AllowUnsafeUpdates = allowUnsafeUpdate;              }            }          }        });      }    }    public void Dispose()    {          }    public void Init(HttpApplication context)    {      context.PostAuthenticateRequest += new EventHandler(context_PostAuthenticateRequest);    }    void context_PostAuthenticateRequest(object sender, EventArgs e)    {      if (HttpContext.Current.User.Identity.IsAuthenticated == true)      {        this.AddUser();      }    }  }}

In lines 59-70 there are some basic stuff to be done so that our HttpModule would be called after authentication request. And of course I'm interested knowing that is this current user already authenticated (on line 66). If this user is authenticated then we need to check if it has SPUser (=is user already in SharePoint). If that's not the case then we'll just add it to SharePoint and make it member of some group ("Visitors" in this example). Code in lines 14-52 is for adding the actual user to SharePoint. On line 33 there is some weird string stuff going on... the reason for that is the user identity name is in this case in format: "cn=John Doe, ou=... ". So I just wanted to rip of the John Doe to be name of the newly created user.

If you wonder the AllowUnsafeUpdates change... welll the security model of SharePoint doesn't allow certain changes in GET (=HTTP Method... opposite to POST). You can bypass it by setting AllowUnsafeUpdates = true but of course you don't want it to leave it like that.. so let's clean up our tracks when we're done on line 46.

Again my code example is meant to be just example. In real life you probably want to add more checks (to line 66 or to line 16) so that the overall performance would be as good as possible.I don't know the performance cost of SPContent.Current.Web.CurrentUser but this kind of stuff needs to be well tested before taking into production.

Anyways... Happy hacking!

J

Comments

  • Anonymous
    June 24, 2007
    PingBack from http://www.virtual-generations.com/2007/06/25/sharepoint-link-love-6-25-2007/

  • Anonymous
    July 30, 2007
    The comment has been removed

  • Anonymous
    July 30, 2007
    The comment has been removed

  • Anonymous
    August 03, 2007
    I am curious where you embedded the final code and how you embedded it.  Did you need visual studio on the actual environment to do it?  I have never attempted to embed code in Sharepoint, so I am sorry if this is a really simple question.

  • Anonymous
    August 05, 2007
    Hi Becky! That code was compiled to DLL that was then deployed to the target SharePoint installation. Only "special" thing that was needed to do was to add new module under <httpModules> at the web.config. But of course this doesn't mean that you need to have Visual Studio at the server, just at your development machine. I hope I answered your question. Anyways.... Happy hacking! J

  • Anonymous
    August 06, 2007
    The comment has been removed

  • Anonymous
    August 07, 2007
    The comment has been removed

  • Anonymous
    February 15, 2008
    Is there any way I can get a copy of the original AccessDenied.aspx page?  My edits to that page completely messed it up. (I did backup the apge before I edited, but for some reason it is still messed up).

  • Anonymous
    March 08, 2008
    Hi! how i can run the solution? what's the procedure to  test the code. thanks :)

  • Anonymous
    November 02, 2008
    I developed something similar that includes information to help you to setup the sites. http://icodehead.blogspot.com/ Burt

  • Anonymous
    May 01, 2011
    Did u try by linking AD group to SP Group and adding user dynamically to AD Group

  • Anonymous
    October 03, 2011
    <a href="j2me-aspnet.blogspot.com/">nice post<a/>

  • Anonymous
    June 19, 2013
    how do u unlock facebook or use facebook in school? i need a website to go on it