DEV Community

Cover image for CodeBehind 2.7 Released, Role Access Added!
elanatframework
elanatframework

Posted on

CodeBehind 2.7 Released, Role Access Added!

A new version of CodeBehind 2.7 has been released with a focus on roles and security. In this article, we'll look at how to configure CodeBehind role access in an ASP.NET Core application.

With the addition of role support, CodeBehind is now a powerful security framework that provides fine-grained control over access to your ASP.NET Core application. Assigning roles allows you to define roles and permissions for different users and restrict access to specific routes, queries, and forms.

Manage roles in CodeBehind

To manage roles and determine access, it is necessary to configure the Program.cs class as follows:

Config CodeBehind role access in ASP.NET Core

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDistributedMemoryCache();

builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30);
});

var app = builder.Build();

app.UseSession();

SetCodeBehind.CodeBehindCompiler.Initialization();

+app.UseRoleAccess(true);

app.UseCodeBehind(true);

app.Run();
Enter fullscreen mode Exit fullscreen mode

To activate the CodeBehind roles, you must also activate the session service.

Please note that the UseRoleAccess middleware must be added before the UseCodeBehind or UseCodeBehindRoute middleware. Also, if you want the access of static files to be checked, you must add the UseStaticFiles middleware after the UseRoleAccess middleware.

As you can see, UseRoleAccess method is initialized with true input argument; this means that automatically if access to the path is not possible, the error page with code 403 will be displayed to the user.

role.xml file

If you are using CodeBehind version 2.7 and later, if you start a new project or restart an existing project, a role.xml file will be created for you in the code_behind directory.

The contents of the default role.xml file are as follows:

<?xml version="1.0" encoding="utf-8" ?>
<role_list>
    <role name="guest" active="false">
        <deny active="false" reason="only administrators have access to the admin path">
            <path match_type="start">/admin</path>
        </deny>
        <action type="static" name="write_html" value="true" active="false" reason="inability to write html tags" />
        <action type="session" name="maximum_login_try" value="10" active="false" reason="the maximum possible number of login attempts has been reached" />
    </role>
    <role name="admin" active="false"></role>
</role_list>
Enter fullscreen mode Exit fullscreen mode

The role.xml file is the user role access configuration. In this file, you can create all kinds of roles and prevent roles from accessing different paths. This file is read only once in the first run of the program; therefore, the changes in this file during the execution of the program have no effect and the program needs to be restarted.

For better understanding, let's change this file a little and make it more concise.

<?xml version="1.0" encoding="utf-8" ?>
<role_list>
    <role name="guest">
        <deny>
            <path match_type="start">/admin</path>
        </deny>
    </role>
</role_list>
Enter fullscreen mode Exit fullscreen mode

According to the code above, the roles are added inside the role_list tag. To add a new role, we add a tag named role and put the name of the role in the name attribute. In the above codes, there is a role named guest. Inside the role tag is the deny tag and inside the deny tag is the path tag which has an attribute named match_type whose value is start and the content inside this tag is the /admin path. This means that the guest role does not have access to the path that starts with /admin.

Path, Query, Form

You can define 3 tags inside the deny tag:

  • path tag
  • query tag
  • form tag

Each of the above tags must have an attribute named match_type that has one of the following values:

  • start: Matches when the requested path starts with the specified string
  • end: Matches when the requested path ends with the specified string
  • exist: Matches when the specified path exists, regardless of its position in the requested path
  • regex: The regex match type is used to match the requested path using a regular expression pattern
  • full_match: The regex match type is used to match the requested path using a regular expression pattern

Example:

Requested route: example.com/admin/settings

  • start: /admin Matches because the requested path starts with "/admin"
  • end: /settings Matches because the requested path ends with "/settings"
  • exist: /admin Matches because "/admin" exists in the requested path
  • regex: /admin/[a-z]+ Matches because the requested path matches the regular expression pattern "/admin/[a-z]+"
  • full_match: /admin/settings Matches because the requested path exactly matches "/admin/settings"

The path tag is for the requested path.

Example:

example.com/admin

The query tag is for querystring.

Example:

example.com/?value=active

The form tag is also for form data.

Example:

Form data is sent when the post method is used in the form tag in HTML.

<form action="/" method="post">
  <label for="fname">First name:</label>
  <input type="text" id="fname" name="fname"><br><br>
  <label for="lname">Last name:</label>
  <input type="text" id="lname" name="lname"><br><br>
  <input type="submit" value="Submit">
</form>
Enter fullscreen mode Exit fullscreen mode

The above form submit sends values ​​similar to the below in the form data:
fname=Cristiano&lname=Ronaldo

Simultaneous use of path, query and form tags

The simultaneous use of each of these tags along with one or two other tags means that the request must meet all the conditions at the same time.

Example:

<?xml version="1.0" encoding="utf-8" ?>
<role_list>
    <role name="guest">
        <deny>
            <path match_type="start">/admin</path>
            <query match_type="exist">value=active</path>
        </deny>
    </role>
</role_list>
Enter fullscreen mode Exit fullscreen mode

In the example above, when the path /admin is requested and the value=active query is established, there is no access for the author role.

In the role tag, in addition to the deny tag, you can also use the action tag. This tag is in two types, static type and session type. You can change session actions based on user behavior. But static action tags are used for all users of the same role.

Note: unlike the deny tag, the action tag is not activated automatically and you must check its value for different actions in the program.

Example of use for action of static type

Prevent access to the stored procedure for a role

In this example, two action tag of static type has been added in the role of guest, whose the name of one of them is update_comment and the other one is add_content_image and their values ​​are stored_procedure.

<?xml version="1.0" encoding="utf-8" ?>
<role_list> 
    <role name="guest">
        <action type="static" name="update_comment" value="deny_stored_procedure" />
        <action type="static" name="add_content_image" value="deny_stored_procedure" />
    </role>
</role_list>
Enter fullscreen mode Exit fullscreen mode

The code below is an example of the database class, which has two methods, GetProcedure and SetProcedure. These two methods automatically check that the action is of the static type with the same name as the procedure, which has the value deny_stored_procedure; so that it does not execute the procedure if it exists.

DataBase class

using CodeBehind;

public class DataBase
{
    private readonly ISession _Session;

    public DataBase(ISession Session)
    {
        _Session = Session;
    }

    public SqlDataReader GetProcedure(string ProcedureName, List<string> ParametersName = null, List<string> ParametersValue = null)
    {
        RoleAccess access = new RoleAccess(_Session);
        bool AccessToPocedure = (access.GetStaticAction(PocedureName) == "deny_stored_procedure");

        if (!AccessToPocedure)
            return null;

        SqlDataReader dr = dbc.GetProcedure(ProcedureName, ParametersName, ParametersValue);
        return dr;
    }

    public void SetProcedure(string ProcedureName, List<string> ParametersName = null, List<string> ParametersValue = null)
    {
        RoleAccess access = new RoleAccess(_Session);
        bool AccessToPocedure = (access.GetStaticAction(PocedureName) == "stored_procedure");

        if (!AccessToPocedure)
            return;

        dbc.SetProcedure(ProcedureName, ParametersName, ParametersValue);
    }
}
Enter fullscreen mode Exit fullscreen mode

Note: with some creativity, the above example can be changed so that the roles only execute actions with the allow_stored_procedure value.

Example of use for action of session type

Comment sending limit for a role

In this example, an action tag of session type has been added in the role of guest, whose name is comment_send_limitation and its value is 3.

<?xml version="1.0" encoding="utf-8" ?>
<role_list> 
    <role name="guest">
        <action type="session" name="comment_send_limitation" value="3" reason="the maximum possible number of send comments has been reached" />
    </role>
</role_list>
Enter fullscreen mode Exit fullscreen mode

In the Controller, we check the value of this action, if it is greater than 0, it sends the comment and then subtracts one from the value of the action. In this example, if the user posts a comment 3 times, the action value will be 0 and he will no longer be able to post more comments.

Controller

using CodeBehind;

public partial class CommentController : CodeBehindController
{
    public void PageLoad(HttpContext context)
    {
        RoleAccess access = new RoleAccess(context.Session);
        int CommentSendLimitation = access.GetSessionAction("comment_send_limitation").ToNumber();

        if (CommentSendLimitation > 0)
        {
            // ...
            // Codes For Sending Comment
            // ...

            access.SetSessionAction("comment_send_limitation", CommentSendLimitation - 1);
        }
        else
            Write("you can no longer send comment");
    }
}
Enter fullscreen mode Exit fullscreen mode

Retrieving the values ​​of the role.xml file

The code below re-initializes the values ​​of the role.xml file in the program.

Example:

new FillRoleList().Set();
Enter fullscreen mode Exit fullscreen mode

Running the above code has no effect on actions by session type. Therefore, please be careful when using this code and consider security issues.

Note: Calling the Set method from the FillRoleList class updates the changes in the role.xml file, but if you have used the session actions, the user is in sync with the session actions until the session is active. So it is better to restart the program for more security.

New option

In version 2.7 of the CodeBehind framework, the default role option has been added to the options file, the default value of which is guest. By default, any user who enters your web application will have the role of guest.

Options file

[CodeBehind options]; do not change order
view_path=wwwroot
move_view_from_wwwroot=true
rewrite_aspx_file_to_directory=false
access_aspx_file_after_rewrite=false
ignore_default_after_rewrite=true
start_trim_in_aspx_file=true
inner_trim_in_aspx_file=true
end_trim_in_aspx_file=true
set_break_for_layout_page=true
convert_cshtml_to_aspx=false
show_minor_errors=false
error_page_path=/error.aspx/{value}
prevent_access_default_aspx=false
+default_role=guest
Enter fullscreen mode Exit fullscreen mode

Change role

You can change the user role in your application. Changing the user role is usually done when registering username and password information on the login page.

The following code changes the default role (guest) to the admin role:

Set new role

new RoleAccess(context.Session).SetUserNewRole("admin");
Enter fullscreen mode Exit fullscreen mode

To exit the user from the current role to the default role (guest), use the following code:

Exit role to default role

new RoleAccess(context.Session).ExitRoleToDefault();
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this article, we have covered the basics of CodeBehind role access in an ASP.NET Core application. We have seen how to configure the CodeBehind service, create roles and deny access to specific paths, queries, and forms. We have also learned about the different types of actions that can be taken based on user behavior, such as static and session-based actions. Additionally, we have discussed how to retrieve the values of the role.xml file and how to change the default role of a user. With CodeBehind, you can create a robust security system that protects your application from unauthorized access.

Version 2.7.1

This version 2.7 had a typo for naming the UseRollAccess middleware and it was corrected to UseRoleAccess in version 2.7.1.

Related links

CodeBehind on GitHub:
https://github.com/elanatframework/Code_behind

CodeBehind in NuGet:
https://www.nuget.org/packages/CodeBehind/

CodeBehind page:
https://elanat.net/page_content/code_behind

Top comments (0)