Back to Devexpress

Authorization Logic — Dashboard

aspnet-405155-security-considerations-authorization-dashboard.md

latest13.6 KB
Original Source

Authorization Logic — Dashboard

  • Mar 06, 2025
  • 5 minutes to read

Use the strategies outlined in this topic to introduce authorization logic for your DevExpress ASP.NET WebForms-powered Dashboard app (and address CWE-285-related security risks).

Implement Authorization Logic

The DevExpress ASP.NET WebForms Dashboard can use both standard Web Forms APIs or DashboardConfigurator APIs to exchange data with the server. The UseDashboardConfigurator property specifies desired data exchange mode and defines the authorization mechanism used.

If the UseDashboardConfigurator property is set to false (default), you must use standard ASP.NET access restriction logic to configure authorized access:

aspx
<location path="Authorization/Dashboards">
    
        <authorization>
           <deny users="?" />
        </authorization>
    
</location>

Standard ASP.NET authorization logic cannot be used if the UseDashboardConfigurator property is enabled. If UseDashboardConfigurator is set to true, you must create a custom dashboard storage that specifies appropriate access rules and implements the IEditableDashboardStorage interface. Use the following class implementation as a starting point and modify it based on your requirements:

Show the DashboardStorageWithAccessRules class

cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Web;
using System.Xml.Linq;
using DevExpress.DashboardWeb;

namespace SecurityBestPractices.Authorization.Dashboards {
    public class DashboardStorageWithAccessRules : IEditableDashboardStorage {
        readonly DataSet dashboards = new DataSet();

        const string dashboardLayoutColumn = "DashboardXml";
        const string nameColumn = "DashboardName";
        const string dashboardIDColumn = "DashboardID";
        readonly Dictionary<string, HashSet<string>> authDictionary = new Dictionary<string, HashSet<string>>();

        public DashboardStorageWithAccessRules() {
            DataTable table = new DataTable("Dashboards");
            DataColumn idColumn = new DataColumn(dashboardIDColumn, typeof(int)) {
                AutoIncrement = true,
                AutoIncrementSeed = 1,
                Unique = true,
                AllowDBNull = false
            };

            table.Columns.Add(idColumn);
            table.Columns.Add(dashboardLayoutColumn, typeof(string));
            table.Columns.Add(nameColumn, typeof(string));
            table.PrimaryKey = new[] { idColumn };
            dashboards.Tables.Add(table);

            // Your logic to get dashboard layouts from the database
            var adminId = AddDashboardCore(XDocument.Load(HttpContext.Current.Server.MapPath(@"~/App_Data/AdminDashboard.xml")), "Admin Dashboard");
            var johnId = AddDashboardCore(XDocument.Load(HttpContext.Current.Server.MapPath(@"~/App_Data/JohnDashboard.xml")), "John Dashboard");

            authDictionary.Add("Admin", new HashSet<string>(new [] { adminId, johnId })); // Admin can view/edit all dashboards
            authDictionary.Add("John", new HashSet<string>(new[] { johnId })); // John can view/edit only his dashboard
        }
        string AddDashboardCore(XDocument dashboard, string dashboardName) {
            DataRow newRow = dashboards.Tables[0].NewRow();
            newRow[nameColumn] = dashboardName;
            newRow[dashboardLayoutColumn] = dashboard; 
            dashboards.Tables[0].Rows.Add(newRow);
            return newRow[dashboardIDColumn].ToString();
        }

        public bool IsAuthorized(string dashboardId) {
            var identityName = GetIdentityName();
            if(!string.IsNullOrEmpty(identityName)) {
                return authDictionary.ContainsKey(identityName) && authDictionary[identityName].Contains(dashboardId);
            }

            return false;
        }

        static string GetIdentityName() {
            return HttpContext.Current.User?.Identity?.Name;
        }

        // Storage implementation
        XDocument IDashboardStorage.LoadDashboard(string dashboardId) {
            if (!IsAuthorized(dashboardId))
                return null;

            // Your logic to get dashboard bytes from the database by <dashboardId>

            DataRow currentRow = dashboards.Tables[0].Rows.Find(dashboardId);
            if (currentRow == null)
                return null;

            XDocument dashboardXml = XDocument.Parse(currentRow[dashboardLayoutColumn].ToString());
            return dashboardXml;
        }

        IEnumerable<DashboardInfo> IDashboardStorage.GetAvailableDashboardsInfo() {
            List<DashboardInfo> dashboardInfos = new List<DashboardInfo>();
            foreach (DataRow row in dashboards.Tables[0].Rows) {
                var dashboardId = row[dashboardIDColumn].ToString();
                if (IsAuthorized(dashboardId)) {
                    DashboardInfo dashboardInfo = new DashboardInfo {
                        ID = row[dashboardIDColumn].ToString(),
                        Name = row[nameColumn].ToString()
                    };
                    dashboardInfos.Add(dashboardInfo);
                }
            }

            return dashboardInfos;
        }

        void IDashboardStorage.SaveDashboard(string dashboardId, XDocument dashboard) {
            if (!IsAuthorized(dashboardId))
                return;

            // Your logic to save dashboard bytes to the database by <dashboardId>

            DataRow currentRow = dashboards.Tables[0].Rows.Find(dashboardId);
            if (currentRow == null)
                return;

            currentRow[dashboardLayoutColumn] = dashboard;
        }
        string IEditableDashboardStorage.AddDashboard(XDocument dashboard, string dashboardName) {
            var identityName = GetIdentityName();

            if(string.IsNullOrEmpty(identityName))
                throw new UnauthorizedAccessException();

            if(!authDictionary.ContainsKey(identityName)) {
                authDictionary.Add(identityName, new HashSet<string>());
            }

            var id = AddDashboardCore(dashboard, dashboardName);
            authDictionary[identityName].Add(id);
            return id;
        }
    }
}
vb
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Web
Imports System.Xml.Linq
Imports DevExpress.DashboardWeb

Namespace SecurityBestPractices.Authorization.Dashboards
    Public Class DashboardStorageWithAccessRules Inherits IEditableDashboardStorage
        ReadOnly dashboards As DataSet = New DataSet()

        Const dashboardLayoutColumn As String = "DashboardXml"
        Const nameColumn As String = "DashboardName"
        Const dashboardIDColumn As String = "DashboardID"
        ReadOnly authDictionary As Dictionary(Of String, HashSet(Of String)) = New Dictionary(Of String, HashSet(Of String))()

        Public Sub New()
            Dim table As DataTable = New DataTable("Dashboards")
            Dim idColumn As DataColumn = New DataColumn(dashboardIDColumn, GetType(Integer)) With {
                .AutoIncrement = True,
                .AutoIncrementSeed = 1,
                .Unique = True,
                .AllowDBNull = False
            }

            table.Columns.Add(idColumn)
            table.Columns.Add(dashboardLayoutColumn, GetType(String))
            table.Columns.Add(nameColumn, GetType(String))
            table.PrimaryKey = {idColumn}
            dashboards.Tables.Add(table)

            ' Your logic to get dashboard layouts from the database
            Dim adminId = AddDashboardCore(XDocument.Load(HttpContext.Current.Server.MapPath("~/App_Data/AdminDashboard.xml")), "Admin Dashboard")
            Dim johnId = AddDashboardCore(XDocument.Load(HttpContext.Current.Server.MapPath("~/App_Data/JohnDashboard.xml")), "John Dashboard")

            authDictionary.Add("Admin", New HashSet(Of String)({adminId, johnId})) ' Admin can view/edit all dashboards
            authDictionary.Add("John", New HashSet(Of String)({johnId})) ' John can view/edit only his dashboard
        End Sub

        Private Function AddDashboardCore(ByVal dashboard As XDocument, ByVal dashboardName As String) As String
            Dim newRow As DataRow = dashboards.Tables(0).NewRow()
            newRow(nameColumn) = dashboardName
            newRow(dashboardLayoutColumn) = dashboard
            dashboards.Tables(0).Rows.Add(newRow)
            Return newRow(dashboardIDColumn).ToString()
        End Function

        Public Function IsAuthorized(ByVal dashboardId As String) As Boolean
            Dim identityName = GetIdentityName()
            If Not String.IsNullOrEmpty(identityName) Then
                Return authDictionary.ContainsKey(identityName) AndAlso authDictionary(identityName).Contains(dashboardId)
            End If

            Return False
        End Function

        Private Shared Function GetIdentityName() As String
            Return HttpContext.Current.User?.Identity?.Name
        End Function

        ' Storage implementation
        Private Function LoadDashboard(ByVal dashboardId As String) As XDocument
            If Not IsAuthorized(dashboardId)
                Then Return Nothing

            ' Your logic to get dashboard bytes from the database by <dashboardId>
            Dim currentRow As DataRow = dashboards.Tables(0).Rows.Find(dashboardId)
            If currentRow Is Nothing
                Then Return Nothing

            Dim dashboardXml As XDocument = XDocument.Parse(currentRow(dashboardLayoutColumn).ToString())
            Return dashboardXml
        End Function

        Private Function GetAvailableDashboardsInfo() As IEnumerable(Of DashboardInfo)
            Dim dashboardInfos As List(Of DashboardInfo) = New List(Of DashboardInfo)()
            For Each row As DataRow In dashboards.Tables(0).Rows
                Dim dashboardId = row(dashboardIDColumn).ToString()
                If IsAuthorized(dashboardId) Then
                    Dim dashboardInfo As DashboardInfo = New DashboardInfo With {
                        .ID = row(dashboardIDColumn).ToString(),
                        .Name = row(nameColumn).ToString()
                    }
                    dashboardInfos.Add(dashboardInfo)
                End If
            Next

            Return dashboardInfos
        End Function

        Private Sub SaveDashboard(ByVal dashboardId As String, ByVal dashboard As XDocument)
            If Not IsAuthorized(dashboardId)
                Then Return

            ' Your logic to save dashboard bytes to the database by <dashboardId>

            Dim currentRow As DataRow = dashboards.Tables(0).Rows.Find(dashboardId)
            If currentRow Is Nothing
                Then Return

            currentRow(dashboardLayoutColumn) = dashboard
        End Sub

        Private Function AddDashboard(ByVal dashboard As XDocument, ByVal dashboardName As String) As String
            Dim identityName = GetIdentityName()

            If String.IsNullOrEmpty(identityName)
                Then Throw New UnauthorizedAccessException()

            If Not authDictionary.ContainsKey(identityName) Then
                authDictionary.Add(identityName, New HashSet(Of String)())
            End If

            Dim id = AddDashboardCore(dashboard, dashboardName)
            authDictionary(identityName).Add(id)
            Return id
        End Function
    End Class
End Namespace

Register your custom dashboard storage in the Global.asax.cs or Global.asax.vb file as follows:

cs
DashboardConfigurator.Default.SetDashboardStorage(new DashboardStorageWithAccessRules());
DashboardConfigurator.Default.CustomParameters += (o, args) => {
    if (!new DashboardStorageWithAccessRules().IsAuthorized(args.DashboardId))
        throw new UnauthorizedAccessException();
};
vb
DashboardConfigurator.[Default].SetDashboardStorage(New DashboardStorageWithAccessRules())
DashboardConfigurator.[Default].CustomParameters += Sub(o, args)
    If Not New DashboardStorageWithAccessRules().IsAuthorized(args.DashboardId) 
        Then Throw New UnauthorizedAccessException()
End Sub

Restrict Access to Data Connections and Tables

The DevExpress Dashboard control allows users to browse available data connections/tables when using its integrated Query Builder. Refer to the following topic to restrict access to these connections/tables: Authorization Logic — Query Builder.

See Also