Take the 2-minute tour ×
SharePoint Stack Exchange is a question and answer site for SharePoint enthusiasts. It's 100% free, no registration required.

I have a solution, written by another developer, that I upgraded from SharePoint 2010 to 2013. One feature uses a connection string from Web.Config for the web application to talk to the Membership database for FBA, to do Entity Framework operations on it, add users, etc. The feature works in 2010. Now that I have upgraded the feature, the database connection no longer works. Other parts of the solution I haven't mentioned, including the actual FBA functionality, do work. All the entity framework code is in a separate project in the solution and the web application pool account has more than enough permissions on the actual database in question.

When I attempt to use the feature that is broken I get an error message:

The underlying provider failed on Open

Which is about as helpful as a hole in a boat. Seeing it, though, I tried changing the connection string a few different ways and finally thought to use Debug mode to see if there was an inner exception. There was! This exception told me that "anonymous user" was trying to access the database and that was the no-go point. Much more useful to me than the original error message. My problem, now, though, is that my code hasn't changed between versions and overall I only did things to update the solution to VS2012 and SharePoint 2013. This particular library didn't get changed at all. Why is it running as anonymous, when the App pool is running as UserX? How can I fix this? Did something change between 2010 and 2013?

UserRequest.aspx.cs Code:

using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using MembershipHelper;
using System.Linq;
using System.Configuration;
using System.Web.Security;


namespace namespace.fba
{
    public partial class UserRequest : LayoutsPageBase
    {

        #region event handlers
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                LoadUI();
            }
        }
        protected void Submit_Click(object sender, EventArgs e)
        {
            CreateUser();
        }
        #endregion

        #region Helping Methods
        private void CreateUser()
        {
            try
            {
                if (CanCreateNewUser)
                {

                    if (Helper.CreateNewUser(txtUserName.Text, txtPassword.Text, txtEmail.Text, System.Configuration.ConfigurationManager.AppSettings["EntityConnectionStringName"]))
                    {
                        txtErrorMsg.Text = "New user has been created";
                    }
                    else
                    {
                        txtErrorMsg.Text = "UserName already exists or error creating new user";
                    }
                }
                LoadUI();
            }
            catch (Exception ex)
            {
                txtErrorMsg.Text = ex.Message;
            }
        }
        private void LoadUI()
        {
            btnSubmit.Enabled = CanCreateNewUser;
            lblMaxUserAllowed.Text = System.Configuration.ConfigurationManager.AppSettings["MaxUsersAllowed"];
        }

        private bool CanCreateNewUser
        {
            get
            {
                try
                {
                    lblUserCount.Text = Helper.GetExistingUsersCount(System.Configuration.ConfigurationManager.AppSettings["EntityConnectionStringName"]).ToString();
                    bool val = (
                        int.Parse(lblUserCount.Text) < int.Parse(System.Configuration.ConfigurationManager.AppSettings["MaxUsersAllowed"]));
                    lblNotice.Visible = !val;
                    return val;
                }
                catch (Exception ex)
                {
                    txtErrorMsg.Text = ex.Message;
                    return false;
                }
            }
        }
        #endregion
    }
}

Helper.cs (This code DOES work in CA, but not in the web application)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.EntityClient;

namespace MembershipHelper
{

    /// <summary>
    /// Static Helper class
    /// </summary>
    public class Helper
    {
        /// <summary>
        /// Returns number of users
        /// </summary>
        /// <param name="connString"></param>
        /// <returns></returns>
        public static int GetExistingUsersCount(string connString)
        {
            using (UserDbEntities cnt = new UserDbEntities(new EntityConnection(System.Configuration.ConfigurationManager.ConnectionStrings[connString].ConnectionString)))
            {
                return cnt.aspnet_Membership.Count();
            }
        }

        /// <summary>
        /// Create new SQL membership user
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="email"></param>
        /// <param name="connString"></param>
        /// <returns></returns>
        public static bool CreateNewUser(string username, string password, string email, string connString)
        {
            try
            {
                return CreateNewUser(username, password, email, true, connString);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>
        /// Create new SQL Membership user
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="email"></param>
        /// <param name="active"></param>
        /// <param name="connString"></param>
        /// <returns></returns>
        public static bool CreateNewUser(string username, string password, string email, bool active, string connString)
        {
            try
            {
                using (UserDbEntities cnt = new UserDbEntities(new EntityConnection(System.Configuration.ConfigurationManager.ConnectionStrings[connString].ConnectionString)))
                {
                   return cnt.CreateUser(username, password, email, active);
                }
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }
...
}
share|improve this question
add comment

2 Answers 2

The issue is down to the good old double problem. When you are accessing the site via a browser using ntlm, SharePoint cannot impersonate the user accessing the site via browser and you access Sql Server as anonymous.

The solution is to impersonate as the web app account as you have done with RunwithElevatedPermissions or use Kerberos. Kerberos has the advantage that allows the web application to access the database as the user who has accessed the website rather than using the app pool account. It is more secure and also more performant due to the way that ntlm will post authentication requests to a DC each time a request for a resource is made.

share|improve this answer
    
I'm not sure I follow you. This same code worked before in SharePoint 2010. So this ntlm problem must be new to 2013. Oh well, +1 for the kerberos suggestion, although I don't think it would work for the FBA users because they don't get direct permission to the database –  Matt May 11 '13 at 2:01
    
You are correct this won't help for fba users. It isn't new for 2013 it might be that your previous environment was all setup for Kerberos. Kerberos needs to be configured for a particular URL. –  Simon Doy May 11 '13 at 7:46
add comment
up vote 0 down vote accepted

Using SPSecurity.RunWithElevatedPermission() solved my problem. The form now seems to work as intended.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.