The Ed-Fi “Classic Dashboards” are no longer supported through the Ed-Fi Alliance. You can still receive support and maintenance through the Ed-Fi vendor community. Please look at any of the vendors’ dashboard solutions on the Registry of Ed-Fi Badges or the Ed-Fi Starter Kits if you are looking for a visualization solution to use with the Ed-Fi ODS. This documentation will remain available to assist existing Classic Dashboard implementers.

Claims-Based Authorization

The security system in the Ed-Fi Dashboards application is implemented using a combination of claims-based security and ownership-based permissions. Claims-based security is implemented using Windows Identity Foundation (WIF), while ownership-based permissions makes use of dashboard data to determine the relationship a user has to the data they are attempting to view.

The security implementation uses software development techniques such as Inversion of Control (IoC) and Aspect-Oriented Programming (AOP), which are discussed in detail in the Architectural Pattern Overview section of this documentation.

Claims-Based Security

Claims-based security is used to restrict access to the data a user may retrieve based on the claims they have been issued by an Identity Provider. Teachers will generally be issued claims enabling them only to see certain pages and information about students in their care, while district superintendents will be able to see everything at their district.

When a user logs on, they are issued claims by the Security Token Service Identity Provider (STS-IP). Depending on the configuration of the STS-IP and the Ed-Fi Dashboards, the claims returned from the STS-IP could be solely identity claims (e.g., name, e-mail address) or the returned claims could be a combination of identity claims and dashboard-specific claims that are used to determine the scope of information the user is able to view.

The Ed-Fi-specific claims at the time of this writing are:

ClaimDescription 
StaffUSIThe unique identifier in the EdFi_Dashboard database for a staff memberhttp://edfi.org/dashboards/identity/claims/staffUSI
LocalEducationAgencyIdThe unique identifier for the local education agency with which the staff member is associatedhttp://edfi.org/dashboards/identity/claims/localEducationAgencyId
FullNameThe full name of the staff memberhttp://edfi.org/dashboards/identity/claims/fullName
ViewAllMetricsThe ability to view all metrics of local education agencies and schools with which the user is associatedhttp://edfi.org/dashboards/identity/claims/org/viewAllMetrics
ViewMyMetricsThe ability to view metrics associated with the staff memberhttp://edfi.org/dashboards/identity/claims/org/viewMyMetrics
ViewAllStudentsThe ability to view all the information and metrics of students associated with the same local education agencies and/or schools with which the staff member is associatedhttp://edfi.org/dashboards/identity/claims/org/viewAllStudents
ViewMyStudentsThe ability to view all the information and metrics of students associated with the staff member through class sections and cohortshttp://edfi.org/dashboards/identity/claims/org/viewMyStudents
ViewAllTeachersThe ability to view the complete staff and teacher list of local education agencies and/or schools associated with the staff memberhttp://edfi.org/dashboards/identity/claims/org/viewAllTeachers
ViewOperationalDashboardThe ability to view the operational dashboard at the associated local education agencies and/or schools of local education agencies and/or school with which the staff member is associatedhttp://edfi.org/dashboards/identity/claims/org/viewOperationalDashboard
ManageGoalsThe ability to view goal management pages and make changes to the goals for the local education agencies and/or schools with which the user is associatedhttp://edfi.org/dashboards/identity/claims/org/manageGoals
AccessOrganizationDefines the education organization to which the claims correspondhttp://edfi.org/dashboards/identity/claims/org/AccessOrganization
ImpersonatingThis is set to the StaffUSI of the staff member that is currently being impersonatedhttp://edfi.org/dashboards/identity/claims/Impersonating


The code snippet below shows all the Ed-Fi-specific claims in use at the time of this writing:

/// <summary>
/// Defines the claim types used by the EdFi dashboards application.
/// </summary>
public static class EdFiClaimTypes
{
    public const string _BaseNamespace = "http://edfi.org/dashboards/identity/claims/";
    public const string _OrgClaimNamespace = _BaseNamespace + "org/";
    public const string StaffUSI = _BaseNamespace + "staffUSI";                        
    public const string LocalEducationAgencyId = _BaseNamespace + "localEducationAgencyId"; 
    public const string FullName = _BaseNamespace + "fullName";                        
    public const string ViewAllMetrics = _OrgClaimNamespace + "viewAllMetrics";
    public const string ViewMyMetrics = _OrgClaimNamespace + "viewMyMetrics";
    public const string ViewAllStudents = _OrgClaimNamespace + "viewAllStudents";
    public const string ViewMyStudents = _OrgClaimNamespace + "viewMyStudents";
    public const string ViewAllTeachers = _OrgClaimNamespace + "viewAllTeachers";
    public const string ViewOperationalDashboard = _OrgClaimNamespace + "viewOperationalDashboard";
    public const string AdministerDashboard = _OrgClaimNamespace + "administerDashboard";
    public const string ManageGoals = _OrgClaimNamespace + "manageGoals";
    public const string AccessOrganization = _OrgClaimNamespace + "AccessOrganization";
    public const string Impersonating = _BaseNamespace + "Impersonating";
}

These claims have been grouped together in a number of claim sets. Assigning a claim set to a user is the most common method of assigning claims to a user. These claim sets are defined in the EdFi.Dashboards.Resources.Security.Implementations.DefaultClaimSetBasedClaimsProvider.

educationOrganizationId

In the table below, an educationOrganizationId could refer to a localEducationAgencyId or schoolId. This data is stored in the domain.StaffEducationOrgInformation table in the EdFi_Dashboard database.
Claim SetClaims
SystemAdministrator

EdFiClaimTypes.AccessOrganization for the associated localEducationAgencyId
EdFiClaimTypes.ViewAllMetrics for the associated localEducationAgencyId
EdFiClaimTypes.ViewAllStudents for the associated localEducationAgencyId
EdFiClaimTypes.ViewAllTeachers for the associated localEducationAgencyId
EdFiClaimTypes.ViewOperationalDashboard for the associated localEducationAgencyId
EdFiClaimTypes.ManageGoals for the associated localEducationAgencyId
EdFiClaimTypes.AdministerDashboard for the associated localEducationAgencyId
EdFiClaimTypes.ManageWatchList for the associated localEducationAgencyId

Superintendent

EdFiClaimTypes.AccessOrganization for the associated educationOrganizationId
EdFiClaimTypes.ViewAllMetrics for the associated educationOrganizationId
EdFiClaimTypes.ViewAllStudents for the associated educationOrganizationId
EdFiClaimTypes.ViewAllTeachers for the associated educationOrganizationId
EdFiClaimTypes.ViewOperationalDashboard for the associated educationOrganizationId
EdFiClaimTypes.ManageGoals for the associated educationOrganizationId
EdFiClaimTypes.ManageWatchList for the associated educationOrganizationId

Principal

EdFiClaimTypes.AccessOrganization for the associated educationOrganizationId
EdFiClaimTypes.ViewAllMetrics for the associated educationOrganizationId
EdFiClaimTypes.ViewMyMetrics for the associated localEducationAgencyId
EdFiClaimTypes.ViewAllStudents for the associated educationOrganizationId
EdFiClaimTypes.ViewAllTeachers for the associated educationOrganizationId
EdFiClaimTypes.ViewOperationalDashboard for the associated educationOrganizationId
EdFiClaimTypes.ManageWatchList for the associated localEducationAgencyId

Administration

EdFiClaimTypes.AccessOrganization for the associated educationOrganizationId
EdFiClaimTypes.ViewAllMetrics for the associated educationOrganizationId
EdFiClaimTypes.ViewMyMetrics for the associated localEducationAgencyId
EdFiClaimTypes.ViewAllStudents, educationOrganizationIdentifier);
EdFiClaimTypes.ViewAllTeachers, educationOrganizationIdentifier);
EdFiClaimTypes.ViewOperationalDashboard, educationOrganizationIdentifier);
EdFiClaimTypes.ManageWatchList for the associated localEducationAgencyId

Leader

EdFiClaimTypes.AccessOrganization for the associated educationOrganizationId
EdFiClaimTypes.ViewAllMetrics for the associated educationOrganizationId
EdFiClaimTypes.ViewMyMetrics for the associated localEducationAgencyId
EdFiClaimTypes.ViewAllStudents for the associated educationOrganizationId
EdFiClaimTypes.ViewAllTeachers for the associated educationOrganizationId

Specialist

EdFiClaimTypes.AccessOrganization for the associated educationOrganizationId
EdFiClaimTypes.ViewMyMetrics for the associated educationOrganizationId
EdFiClaimTypes.ViewMyStudents for the associated educationOrganizationId

Staff

EdFiClaimTypes.AccessOrganization for the associated educationOrganizationId
EdFiClaimTypes.ViewMyMetrics for the associated educationOrganizationId

Two classes exist in the as-shipped code base for assigning claim sets to users.

Class 1. StaffCategoryUserClaimSetsProvider. This provider pulls the staff category and educationOrganizationId from the domain.StaffEducationOrgInformation table in the EdFi_Dashboard database. It then uses the following table to assign a claim set based on the retrieved staffCategory for the staff member.

Staff CategoryClaim Set
LEA System AdministratorClaimsSet.SystemAdministrator
SuperintendentClaimsSet.Superintendent
PrincipalClaimsSet.Principal

Assistant Superintendent
Assistant Principal
Counselor
School Leader

ClaimsSet.Administration

School Administrative Support Staff
LEA Administrative Support Staff
Local Education Agency Administrative Support Staff

ClaimsSet.Leader

LEA Specialist
Local Education Agency Specialist
Teacher
School Nurse
High School Counselor
School Specialist
Specialist/Consultant

ClaimsSet.Specialist

Local Education Agency
LEA Administrator
Clerk
School Administrator

ClaimsSet.Staff

This implementation uses a fairly simplistic mapping between users and claim sets and is not suggested for a production environment. In production, most implementations use the PositionTitleUserClaimSetsProvider described below.

Class 2. PositionTitleUserClaimSetsProvider. This implementation pulls the PositionTitle from the domain.StaffEducationOrgInformation table in the EdFi_Dashboards database. Next, it will look in the dashboards.ClaimSetMapping table in the EdFi_Application database for a matching PositionTitle. Then it will assign the associated claimset to that user. The position-title-to-claim-set mapping can be managed through the administration section of the dashboards individually or from an uploaded CSV file (a template for which is available for download from the UI).

 

This implementation is most commonly used for production deployment since there is more ability to finely tune and configure claim sets to position titles, which greatly outnumber the available staff categories.

The implementations are available to link together in a chain of responsibility, which is the default development registration of an IUserClaimSetsProvider. The default development registration is shown below:

IUserClaimSetsProvider default registration
 protected virtual void RegisterIUserRolesProvider(IWindsorContainer container)
 {
     var registrar = new ChainOfResponsibilityRegistrar(container);


     var types = new[]
     {
         typeof (PositionTitleUserClaimSetsProvider),
         typeof (StaffCategoryUserClaimSetsProvider)
     };

     registrar
         .RegisterChainOf
         <IUserClaimSetsProvider<ClaimsSet, EdFiUserSecurityDetails>,
             NullUserClaimSetsProvider<ClaimsSet, EdFiUserSecurityDetails>>(types);
}

In this case, the code will first check to see if it can find a matching position title in the dashboard.ClaimSetMapping table in the EdFi_Application database. If there is no match, the chain of responsibility then tries to match on the user's staff category.

The actual assigning of a claim set to a user through an IUserClaimSetsProvider implementation can take place in the SecurityTokenService.Web or in the EdFi.Dashboards code. This is generally referred to as Ed-Fi-Dashboards-specific claims enrichment or authorization, which happens after authentication.

In the as-shipped configuration, the Ed-Fi Dashboards perform Ed-Fi-Dashboards-specific claims enrichment in EdFi.Dashboards and have the Security Token Service simply authenticate a user. This is done by registering the IdentityClaimsGetOutputClaimsIdentityProvider for the IGetOutputClaimsIdentityProvider in the SecurityTokenService.Web configuration-specific installer and the EdFi.Dashboards.Resources.Security.Implementations.DashboardClaimsAuthenticationManagerProvider in the EdFi.Dashboards.Presentation.Web application configuration-specific installer

Ownership-Based Permissions

While a user may have the ability to view information for some students, they may not be able to view information about all students. This is where ownership-based security comes into play. Generally speaking, teachers are able to see students currently enrolled in their classes, principals are able to see students currently enrolled in their schools, and superintendents can see any student in the district. There are also other roles in the system, such as district administrators, who are unable to see any student-level information. In the Ed-Fi Dashboards application, AOP techniques are used to add call-level parameter validation to verify user’s data “ownership,” and to filter student lists so that users only see their own students when drilling down into campus metric student lists.

To support AOP, the Castle Windsor container provides an IInterceptor interface which enables method-level call interception, providing a mechanism for writing code that will run before and/or after the target method. When we identify the StudentInformationService class as the desired implementation for the IStudentInformationService interface, we also tell the container that we want the ApplyViewPermissionsInterceptor to be used to intercept the method calls so that we can inspect the parameters plus their values and then, based on the current user’s information, decide whether to allow the call to proceed or not.

With the interceptor now “registered” with the container, when the SomeController class is requested of the container, which depends on an IStudentInformationService instance, the container no longer simply returns an instance of StudentInformationService. Rather, it dynamically creates an IStudentInformationServiceProxy class containing code to instantiate our ApplyViewPermissionsInterceptor class and invoke the Intercept method.

The diagram below shows the classes used in performing method-level interception/validation:

The ApplyViewPermissionsByClaim interceptor then iterates through all the claims that can be used to authorize access to the service method being invoked to see if the user has the claim. For each claim, it then invokes a corresponding {ClaimName}ClaimAuthorization class which will in turn call into claim-specific chain of validator classes (see the Chain of Responsibility pattern). Each claim validator implementation is responsible for validating a specific method signature (a distinct combination of method parameters).

In the dashboards application, all these components are registered with the container using conventions based on names. For example, a service method that can be authorized by a ViewMyStudents claim will be matched to the ViewMyStudentsClaimAuthorization class, which will in turn call into a chain of ViewMyStudentsClaimValidator{Xxxxx} classes, each of which validates a particular method signature.

The Chain of Responsibility pattern provides an extensible mechanism for handling requests. In this case, we are attempting to handle a request to validate a user’s ability to make a method call with a particular set of parameter values. Although not shown on the diagrams above, the Get service method actually takes a request with three arguments: a local education agency ID (int), a school ID (int), and a student ID (int). In the processing for a ViewMyStudents claim, this particular invocation will end up being handled by the ViewMyStudentsClaimValidatorSchoolStudent class:

public class ViewMyStudentsClaimValidatorSchoolStudent : ViewMyStudentsClaimValidator
{
    public ViewMyStudentsClaimValidatorSchoolStudent(ISecurityAssertionProvider securityAssertionProvider, IClaimValidator next) : base(securityAssertionProvider, next)
    {
        HandledSignatureKey = ClaimValidatorRequest.SchoolStudentSignature;
    }
    protected override object HandleRequest(ClaimValidatorRequest request)
    {
        ValidateClaimSchoolStudentByStudentListAssociation(request, ClaimType);
        return null;
    }
}

ViewMyStudentsClaimValidatorSchoolStudent Class

public object ValidateClaimSchoolStudentByStudentListAssociation(ClaimValidatorRequest request, string claimType)
{
    var schoolId = request.GetSchoolId();
    var staff = UserInformation.Current.StaffUSI;
    var studentUSI = request.GetStudentUSI();
 
    SecurityAssertionProvider.CurrentUserMustHaveClaimWithinEducationOrganizationHierarchy(schoolId, claimType);
    SecurityAssertionProvider.StaffMemberMustHaveLinkToStudent(staff, studentUSI);
    ValidateLocalEducationAgencySchoolIfPresent(request);
    return null;
}

You’ll notice in the “helper” method ValidateClaimSchoolStudentByStudentListAssociation, there are two assertions made against the arguments. First, the user must have a claim associated with the school in question. If the user has no ViewMyStudents claim associated with the school, the method call is rejected. Second, the staff member (not necessarily the user) must be associated with the student through a current enrollment or a cohort relationship. If this is not true, the method call is rejected. Finally, a consistency check is made between the local education agency and the school.

If the invocation is handled, and no exceptions are thrown, the interceptor will allow the call to proceed.

Secure Assumptions

The security infrastructure was developed to be “secure by default.” Thus, if a method is added to the services layer which contains a new and as-of-yet unhandled method signature, the security infrastructure will reject invocation attempts on this method.

Additionally, the code base includes unit tests to identify potential gaps in the parameter validation implementations before invocation is ever attempted. For issues identified by the unit test, developers must either address the scenario (by implementing a new claim validator for the particular claim type), or explicitly identify it as a method that does not need to be authorized (some methods do not expose data that is sensitive in nature).

Another scenario that sometimes occurs is where a method might have a request that includes a couple of identifiers, followed by another parameter which only serves to further restrict the returned data, but does not represent a possible security violation. Consider the following request:  

public class HistoricalChartRequest
{
    public int SchoolId { get; set; }
    public int MetricId { get; set; }
        
    [AuthIgnore("PeriodId does not affect the results of the request in a way requiring it to be secured.")]
    public int? PeriodId { get; set; }
 
    ...
}

In this case, we can identify that the PeriodId value will never result in sensitive data being exposed by using the AuthIgnore attribute. The security infrastructure will consider this parameter to be “safe,” and ignore it while trying to find a claim validator matching the method signature.