Archive

Posts Tagged ‘ASP.NET’

ASP.NET MVC: Disable Controller

August 3rd, 2015 Admin No comments

    If you want to disable a MVC Controller, not deleting it, a good way to do that is apply a custom ActionFilter-derived attribute to it. Don’t confuse the ActionFilterAttribute defined in the System.Web.Mvc (the one we’re going to use) with the attribute of the same name residing within the Web API infrastructure, namely in the System.Web.Http.Filters namespace.

So, the custom attribute could be defined like

public class DisableControllerAttribute : ActionFilterAttribute
{
	public override void OnActionExecuting(ActionExecutingContext filterContext)
	{
		//filterContext.Result = new HttpNotFoundResult();
		throw new HttpException((int)HttpStatusCode.NotFound, null);
	}
}

Applying it to the target MVC Controller is as simple as follows

...
using System.Web.Mvc;
...

[DisableController]
public class SomeLegacyController : Controller
{
}

Calling any action of such MVC contoller, user gets the 404 Http Status Code page. The page could look differently depending on which way you use to return the Status Code. Using the first line of code in the DisableControllerAttribute.OnActionExecuting (currently commented), the IIS “native” 404 page will be returned.

IIS Native 404 page not found

Throwing the HttpException like shown in the second line of the DisableControllerAttribute.OnActionExecuting (uncommented), you’ll get the ASP.NET handled page.

ASP.Net 404 page not found

SharePoint: SqlMembershipProvider – Lock User

July 21st, 2013 Admin No comments

    In addition to the article SharePoint: SqlMembershipProvider – Get All Users In Role, here is one more method to extend the SqlMembershipProvider with. It’s found out that the SqlMembershipProvider doesn’t provide a method to lock user. By default a user can be automatically locked after several frequent and failed attempts to login. To unlock such users the SqlMembershipProvider supplies with the UnlockUser method. But what if administrator wants to temporarily lock user for some reason? Unfortunately, there is no such method out-of-box.

So, let’s try to implement our own LockUser method. Two obvious steps for that are as follows: to create a Stored Procedure in database; to extend a class derived from the SqlMembershipProvider with the proper method.


LockUser Stored Procedure

The stored procedure is very simple as we need just to update one field in the aspnet_Membership table for appropriate user. Below is the script to create such procedure. Run the script on MembershipProvider database, in my case it’s aspnetdb.

USE [aspnetdb]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:      .Net Follower
-- Description:	Locks User
-- =============================================
CREATE PROCEDURE [dbo].[aspnet_Membership_LockUser]
	@ApplicationName                         nvarchar(256),
    @UserName                                nvarchar(256)
AS
BEGIN
	DECLARE @UserId uniqueidentifier
    SELECT  @UserId = NULL
    SELECT  @UserId = u.UserId
    FROM    dbo.aspnet_Users u, dbo.aspnet_Applications a, dbo.aspnet_Membership m
    WHERE   LoweredUserName = LOWER(@UserName) AND
            u.ApplicationId = a.ApplicationId  AND
            LOWER(@ApplicationName) = a.LoweredApplicationName AND
            u.UserId = m.UserId

    IF ( @UserId IS NULL )
        RETURN 1

    UPDATE dbo.aspnet_Membership
    SET IsLockedOut = 1 WHERE @UserId = UserId

    RETURN 0
END


Custom Membership Provider

Now we can add the LockUser method to the custom Membership Provider called SqlMembershipProviderEx and shown in the article. The SqlMembershipProviderEx with the LockUser is listed below. Note that the methods mentioned in the previous article are skipped.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Globalization;
using System.Reflection;
using System.Web.Security;
using Microsoft.SharePoint;

namespace dotNetFollower
{
    public class SqlMembershipProviderEx : SqlMembershipProvider
    {
	    ...

        public bool LockUser(string username)
        {
            bool flag = false;
            CheckParameter(ref username, true, true, true, 0x100, "username");

            DoInSqlConnectionContext(delegate(SqlConnection connection)
            {
                //this.CheckSchemaVersion(connection.Connection);
                SqlCommand command = new SqlCommand("dbo.aspnet_Membership_LockUser", connection)
                {
                    CommandTimeout = CommandTimeout,
                    CommandType    = CommandType.StoredProcedure
                };
                command.Parameters.Add(CreateInputParam("@ApplicationName", SqlDbType.NVarChar, ApplicationName));
                command.Parameters.Add(CreateInputParam("@UserName", SqlDbType.NVarChar, username));
                SqlParameter parameter = new SqlParameter("@ReturnValue", SqlDbType.Int)
                {
                    Direction = ParameterDirection.ReturnValue
                };
                command.Parameters.Add(parameter);

                command.ExecuteNonQuery();
                flag = ((parameter.Value != null) ? ((int)parameter.Value) : -1) == 0;
            });

            return flag;
        }

        protected internal static void CheckParameter(ref string param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize, string paramName)
        {
            if (param == null)
            {
                if (checkForNull)
                    throw new ArgumentNullException(paramName);
            }
            else
            {
                param = param.Trim();
                if (checkIfEmpty && (param.Length < 1))
                    throw new ArgumentException(string.Format("The parameter '{0}' must not be empty.", new object[] { paramName }), paramName);

                if ((maxSize > 0) && (param.Length > maxSize))
                    throw new ArgumentException(string.Format("The parameter '{0}' is too long: it must not exceed {1} chars in length.", new object[] { paramName, maxSize.ToString(CultureInfo.InvariantCulture) }), paramName);

                if (checkForCommas && param.Contains(","))
                    throw new ArgumentException(string.Format("The parameter '{0}' must not contain commas.", new object[] { paramName }), paramName);
            }
        }

        ...

    }
}

The latest version of the SqlMembershipProviderEx along with all used additional classes are available to download here.

Related posts:

SharePoint: SqlMembershipProvider – Get All Users In Role

June 30th, 2013 Admin No comments

    In the SharePoint application I’m currently working on, I configured Form Based Authentication (FBA) using the SqlMembershipProvider and SqlRoleProvider. Implementing some user management functionality, I run into the lack of a method to get the users in particular role by portions (so-called pagination). The SqlRoleProvider exposes the GetUsersInRole method which returns only names of users in the passed role and doesn’t support pagination. The direct way in this case is to get user names and then get appropriate users, calling the GetUser method of SqlMembershipProvider one time per name. This approach results in a bunch of requests to the database: one request is to get names of users in a role and a number of requests are to get each user by his name. In addition, we have somehow to implement pagination ourselves. The approach is acceptable, but let’s try to reduce requests to the database and borrow somewhere the pagination logic.


GetAllUsersInRole Stored Procedure

It’s interesting that the SqlMembershipProvider provides the GetAllUsers method that supports pagination. On the database level, every call of SqlMembershipProvider.GetUsersInRole and SqlMembershipProvider.GetAllUsers ends with executing such Stored Procedures as aspnet_UsersInRoles_GetUsersInRoles and aspnet_Membership_GetAllUsers respectively. So, we know that the aspnet_UsersInRoles_GetUsersInRoles searches for names of users in a role while the aspnet_Membership_GetAllUsers is able to return users by portions. Let’s combine these two Stored Procedures and create another one which would select users in a role and return a required portion of the result. The sql script below creates such Stored Procedure, I named it aspnet_Membership_GetAllUsersInRole. Note the script should be executed on MembershipProvider database, it’s aspnetdb in my case.

USE [aspnetdb]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:		.Net Follower
-- Description:	Returns users in role by portions
-- =============================================
CREATE PROCEDURE [dbo].[aspnet_Membership_GetAllUsersInRole]
    @ApplicationName       nvarchar(256),
    @PageIndex             int,
    @PageSize              int,
    @RoleName              nvarchar(256)
AS
BEGIN
    DECLARE @ApplicationId uniqueidentifier
    SELECT  @ApplicationId = NULL
    SELECT  @ApplicationId = ApplicationId FROM dbo.aspnet_Applications WHERE LOWER(@ApplicationName) = LoweredApplicationName
    IF (@ApplicationId IS NULL)
        RETURN 0

	DECLARE @RoleId uniqueidentifier
    SELECT  @RoleId = NULL

    SELECT  @RoleId = RoleId
    FROM    dbo.aspnet_Roles
    WHERE   LOWER(@RoleName) = LoweredRoleName AND ApplicationId = @ApplicationId

    IF (@RoleId IS NULL)
		RETURN 0

    -- Set the page bounds
    DECLARE @PageLowerBound int
    DECLARE @PageUpperBound int
    DECLARE @TotalRecords   int
    SET @PageLowerBound = @PageSize * @PageIndex
    SET @PageUpperBound = @PageSize - 1 + @PageLowerBound

    -- Create a temp table TO store the select results
    CREATE TABLE #PageIndexForUsers
    (
        IndexId int IDENTITY (0, 1) NOT NULL,
        UserId uniqueidentifier
    )

    -- Insert into our temp table
    INSERT INTO #PageIndexForUsers (UserId)
    SELECT u.UserId
    FROM   dbo.aspnet_Membership m, dbo.aspnet_Users u, dbo.aspnet_UsersInRoles ur
    WHERE  u.ApplicationId = @ApplicationId AND u.UserId = m.UserId AND
		   u.UserId = ur.UserId AND @RoleId = ur.RoleId
    ORDER BY u.UserName

    SELECT @TotalRecords = @@ROWCOUNT

    SELECT u.UserName, m.Email, m.PasswordQuestion, m.Comment, m.IsApproved,
            m.CreateDate,
            m.LastLoginDate,
            u.LastActivityDate,
            m.LastPasswordChangedDate,
            u.UserId, m.IsLockedOut,
            m.LastLockoutDate
    FROM   dbo.aspnet_Membership m, dbo.aspnet_Users u, #PageIndexForUsers p
    WHERE  u.UserId = p.UserId AND u.UserId = m.UserId AND
           p.IndexId >= @PageLowerBound AND p.IndexId <= @PageUpperBound
    ORDER BY u.UserName
    RETURN @TotalRecords
END


Custom Membership Provider

Now let’s extend our Membership Provider with a new method that deals with the aspnet_Membership_GetAllUsersInRole. I created a class SqlMembershipProviderEx derived from SqlMembershipProvider and containing the target GetAllUsersInRole method. The class is demonstrated below, but first of all a few remarks on the code:

  • I had to use Reflection to get values of some important fields (like Connection String to the database, for example) as Microsoft makes everything private or internal;
  • The SqlMembershipProvider elevates privileges when opening SqlConnection. Since the extended Membership Provider is going to be used in SharePoint application, I did the same by means of SPSecurity.RunWithElevatedPrivileges. Note however that if you want to use the extended Membership Provider in a pure ASP.Net application you will need to deal with such internal (of course) classes as SqlConnectionHolder and ApplicationImpersonationContext through Reflection;
  • The code of GetAllUsersInRole method is mainly based on the GetAllUsers of the parent SqlMembershipProvider class.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
using System.Web.Security;
using Microsoft.SharePoint;

namespace dotNetFollower
{
    public class SqlMembershipProviderEx : SqlMembershipProvider
    {
        protected string _connectionString;
        protected int?   _sqlCommandTimeout;

        protected string ConnectionString
        {
            get
            {
                return _connectionString ??
                    (_connectionString = Convert.ToString(this.GetFieldValue("_sqlConnectionString")));
            }
        }

        protected int CommandTimeout
        {
            get
            {
                if (_sqlCommandTimeout == null)
                    _sqlCommandTimeout = Convert.ToInt32(this.GetFieldValue("_CommandTimeout"));
                return _sqlCommandTimeout.Value;
            }
        }

        public MembershipUserCollection GetAllUsersInRole(string role, int pageIndex, int pageSize, out int totalRecords)
        {
            if (pageIndex < 0)
                throw new ArgumentException("The pageIndex must be greater than or equal to zero.", "pageIndex");
            if (pageSize < 1)
                throw new ArgumentException("The pageSize must be greater than zero.", "pageSize");

            long num = ((pageIndex * pageSize) + pageSize) - 1;
            if (num > 0x7fffffff)
                throw new ArgumentException("The combination of pageIndex and pageSize cannot exceed the maximum value of System.Int32.", "pageIndex and pageSize");

            MembershipUserCollection users = new MembershipUserCollection();
            int recordsAmount = 0;

            DoInSqlConnectionContext(delegate(SqlConnection connection)
                {
                    //this.CheckSchemaVersion(connection.Connection);
                    SqlCommand command     = new SqlCommand("dbo.aspnet_Membership_GetAllUsersInRole", connection);
                    SqlDataReader reader   = null;
                    SqlParameter parameter = new SqlParameter("@ReturnValue", SqlDbType.Int);
                    command.CommandTimeout = CommandTimeout;
                    command.CommandType    = CommandType.StoredProcedure;
                    command.Parameters.Add(CreateInputParam("@ApplicationName", SqlDbType.NVarChar, ApplicationName));
                    command.Parameters.Add(CreateInputParam("@PageIndex", SqlDbType.Int, pageIndex));
                    command.Parameters.Add(CreateInputParam("@PageSize", SqlDbType.Int, pageSize));
                    command.Parameters.Add(CreateInputParam("@RoleName", SqlDbType.NVarChar, role));
                    parameter.Direction = ParameterDirection.ReturnValue;
                    command.Parameters.Add(parameter);
                    try
                    {
                        reader = command.ExecuteReader(CommandBehavior.SequentialAccess);
                        while (reader.Read())
                        {
                            string   nullableString          = GetNullableString(reader, 0);
                            string   email                   = GetNullableString(reader, 1);
                            string   passwordQuestion        = GetNullableString(reader, 2);
                            string   comment                 = GetNullableString(reader, 3);
                            bool     boolean                 = reader.GetBoolean(4);
                            DateTime creationDate            = reader.GetDateTime(5).ToLocalTime();
                            DateTime lastLoginDate           = reader.GetDateTime(6).ToLocalTime();
                            DateTime lastActivityDate        = reader.GetDateTime(7).ToLocalTime();
                            DateTime lastPasswordChangedDate = reader.GetDateTime(8).ToLocalTime();
                            Guid     providerUserKey         = reader.GetGuid(9);
                            bool     isLockedOut             = reader.GetBoolean(10);
                            DateTime lastLockoutDate         = reader.GetDateTime(11).ToLocalTime();
                            users.Add(new MembershipUser(Name, nullableString, providerUserKey, email, passwordQuestion,
                                                         comment, boolean, isLockedOut, creationDate, lastLoginDate,
                                                         lastActivityDate, lastPasswordChangedDate, lastLockoutDate));
                        }
                    }
                    catch (Exception ex)
                    {
                        EventLogger.WriteError(ex);
                        throw;
                    }
                    finally
                    {
                        if (reader != null)
                            reader.Close();
                        if (parameter.Value is int)
                            recordsAmount = (int)parameter.Value;
                    }
                });
            totalRecords = recordsAmount;
            return users;
        }

        protected void DoInSqlConnectionContext(Action<SqlConnection> action)
        {
            SqlConnection connection = null;
            try
            {
                connection = new SqlConnection(ConnectionString);
                SPSecurity.RunWithElevatedPrivileges(connection.Open);
                action(connection);
            }
            finally
            {
                if (connection != null)
                    connection.Close();
            }
        }

        protected SqlParameter CreateInputParam(string paramName, SqlDbType dbType, object objValue)
        {
            SqlParameter parameter = new SqlParameter(paramName, dbType);
            if (objValue == null)
            {
                parameter.IsNullable = true;
                parameter.Value      = DBNull.Value;
                return parameter;
            }
            parameter.Value = objValue;
            return parameter;
        }

        protected string GetNullableString(SqlDataReader reader, int col)
        {
            return !reader.IsDBNull(col) ? reader.GetString(col) : null;
        }
    }
}

Note the EventLogger class is described in the post SharePoint: Simple Event Logger while the GetFieldValue method is provided by ReflectionHelper described in the C#: How to set or get value of a private or internal field through the Reflection and C#: How to set or get value of a private or internal property through the Reflection.

The latest version of the SqlMembershipProviderEx along with all used additional classes are available to download here.

Related posts:

ASP.NET: How to trigger a validator on the client-side

August 27th, 2012 Admin No comments


ValidatorValidate

The easiest way to trigger a validator on the client-side is to use the ValidatorValidate method provided by ASP.NET as a part of a Validation client-side API. You don’t need to include any js-files so as the all required scripts will be included automatically when you add a validator to your page. The typical use is something like this

var validator = document.getElementById('ctl00_PlaceHolderMain_info_textbox').Validators[0]; // Page_Validators[0];

ValidatorValidate(validator); // fire validation
ValidatorUpdateIsValid(); // update the global Page_IsValid flag

The ValidatorValidate makes the passed validator examine the input element bound to it and updates the validator‘s display (shows or hides its error message). I also recommend to call the ValidatorUpdateIsValid method, which updates the global variable Page_IsValid and by that may prevent the Html-form submit when the validator failed.

Below is a simple JavaScript function I use to force validation of a specified input. The function gets the input by the passed id, then goes through the all validators linked to the input and activates each of them. Finally, it updates the global Page_IsValid flag.

function ForceInputValidation(inputId) {
    var targetedControl = document.getElementById(inputId);    

    if (typeof(targetedControl.Validators) != "undefined") {
        var i;
        for (i = 0; i < targetedControl.Validators.length; i++)
            ValidatorValidate(targetedControl.Validators[i]);
    }

    ValidatorUpdateIsValid();
}

ASP.Net places the initialization of validators at the end of the web page, so, it makes sense to handle the input‘s Validators-collection after the page has been loaded entirely. The following example demonstrates how to fire validation of the specified input right after the page has been loaded. jQuery is a best helper in that.

$(document).ready(function () {
    ForceInputValidation('ctl00_PlaceHolderMain_info_textbox');
});


ValidatorEnable

As some blogs and forums recommend, you can also use another built-in function – ValidatorEnable, the code of which is shown below:

function ValidatorEnable(validator, enable) {
    validator.enabled = (enable != false);
    ValidatorValidate(validator);
    ValidatorUpdateIsValid();
}
// ...
// the typical use to fire validation is
var validator = document.getElementById('ctl00_PlaceHolderMain_info_textbox').Validators[0]; // Page_Validators[0];
ValidatorEnable(validator, true);

The ValidatorEnable calls sequentially the same ValidatorValidate and ValidatorUpdateIsValid functions we use above. Thus to activate one validator you can use, with no difference, either ValidatorEnable or combination of ValidatorValidate and ValidatorUpdateIsValid. But activating several validators (like in ForceInputValidation function) I advise employing the pair of the ValidatorValidate and ValidatorUpdateIsValid functions. That is because the ValidatorUpdateIsValid loops through all validators on the page. So, for best performance we should fire the all required validators and then call ValidatorUpdateIsValid once.

ps here is good source of information about the client-side validation.

SharePoint: How To Maximize a Modal Dialog Window

July 17th, 2012 Admin 3 comments

    In SharePoint 2010 by default a list item is opened in a Modal Dialog Window. The dialog adjusts its size depending on the size of the contained content and provides users with buttons to maximize and close itself. So, I was asked to maximize the dialog when opening some list items.


Creating and Initializing of a Modal Dialog Window

The JavaScript responsible for the dialog rendering is in the SP.UI.Dialog.js file (usually located in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS). Clicking on a link-title, for example, in a list view leads to creating and displaying a new Modal Dialog Window in a manner similar to the following:

var options = SP.UI.$create_DialogOptions();

options.url           = 'display-page corresponding to the current list item';
options.title         = 'list item title';
options.allowMaximize = true;
options.showClose     = true;

// possible option, but it's not used in the place in question
//options.showMaximized = true;

var modalDialog = SP.UI.ModalDialog.showModalDialog(options);

The commented line demonstrates one of the possible settings, showMaximized, which apparently does exactly what we need. So, it would be a good solution if we had control under creation of the dialog. Unfortunately, we don’t have. Modifying scripts in SharePoint system js-files runs counter to all known “best practices”, therefore I don’t even consider it. Another possible way is override the client-side OnClick-events of the link-title fields everywhere (in list views, web parts and so on) so that your own script would create and display the dialog in a required manner. Obviously, it’s a very time-consuming solution. Moreover, every time when creating a list view, web part or something else you always have to keep in mind the possible need to rewrite their OnClick-events. In my opinion the only acceptable approach is allow a web page itself to maximize the dialog window it’s placed in.


Script to Maximize a Modal Dialog Window

For that, first of all, we need to have a JavaScript to maximize the current dialog. As SP.UI.Dialog.js doesn’t provide a function/method intended for use outside, we have to consume undocumented internal functions. A suitable script is described in the blog post – “How to maximize a Modal Dialog in JavaScript?“. With slight changes the JavaScript looks like the following

function _maximizeWindow() {
    var currentDialog = SP.UI.ModalDialog.get_childDialog();
    if (currentDialog != null && !currentDialog.$S_0)
        currentDialog.$z();
    }
    ExecuteOrDelayUntilScriptLoaded(_maximizeWindow, 'sp.ui.dialog.js');

_maximizeWindow gets the current dialog window instance and tries to maximize it if it hasn’t been done before. ExecuteOrDelayUntilScriptLoaded, in turn, is applied to ensure the _maximizeWindow is called after the sp.ui.dialog.js has been completely loaded.


Script Applying

Having the script, we can insert it into the end of a web page to be maximized. It could be done through SharePoint Designer or by modifying a physical aspx-file defined as a display/new/edit form for a list or a content type. However, I decided to add the script dynamically in a page’s code-behind. So, the resultant code is shown below:

using System;
using System.Web.UI;
using Microsoft.SharePoint;

//...

public class DisplayListItem : Page
{
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        if(!IsPostBack)
            AddMaximizeWindowScript();
    }

    //...

    protected virtual void AddMaximizeWindowScript()
    {
        if (SPContext.Current.IsPopUI)
        {
            const string scriptKey = "MaxWinwScript";
            if (!ClientScript.IsClientScriptBlockRegistered(GetType(), scriptKey))
            {
                const string jsScript =
                    @"function _maximizeWindow() {
                        var currentDialog = SP.UI.ModalDialog.get_childDialog();
                        if (currentDialog != null && !currentDialog.$S_0)
                            currentDialog.$z();
                        }
                        ExecuteOrDelayUntilScriptLoaded(_maximizeWindow, 'sp.ui.dialog.js');";

                ClientScript.RegisterClientScriptBlock(GetType(), scriptKey, jsScript, true);
            }
        }
    }

    //...
}

You may ask why I use RegisterClientScriptBlock instead of RegisterStartupScript or window.onload, or jQuery‘s $(document).ready (the latest two arise on client side). The reason is that initially the dialog window is created with the autoSize option. So, it’s very reasonable to maximize the window as soon as possible to prevent the content’s size calculating, which apparently happens after the page has been loaded entirely.