Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Grades Assigning #62

Merged
merged 33 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
feaeb33
Redesign a model for assigned grade
romandykyi Dec 9, 2023
5a00305
Add ActivityType entity and its DTOs
romandykyi Dec 10, 2023
acf7dd3
Implement `ActivityType` service
romandykyi Dec 10, 2023
2bd6970
Implement activity types endpoints
romandykyi Dec 10, 2023
52006a9
Configure test data for `ActivityType`
romandykyi Dec 10, 2023
4de55e6
Update README.md
romandykyi Dec 10, 2023
85631fc
Add DTOs for assigned grades
romandykyi Dec 11, 2023
0a0d339
Make the assigned grades validator class generic
romandykyi Dec 11, 2023
0cb4264
Add DTOs for ClassGrade and validation
romandykyi Dec 11, 2023
53c7e87
Fix error in a test
romandykyi Dec 11, 2023
ae909d2
Create `IAssignedGradesService` interface
romandykyi Dec 11, 2023
8af09cc
Manage namespaces
romandykyi Dec 12, 2023
05c6679
Merge branch 'master' into implement-grades-assigning
romandykyi Dec 25, 2023
b97b853
Merge `ClassGrade` and `AssignedGrade`
romandykyi Dec 25, 2023
5a2d47f
Add `AssignedGrades` table
romandykyi Dec 25, 2023
346663e
Add `AssignedGradeUpdateDto`
romandykyi Dec 25, 2023
23c8080
Implement `AssignedGradesService`
romandykyi Dec 25, 2023
7a71fc2
Implement `AssignedGradesFilter`
romandykyi Dec 25, 2023
f0d17ed
Refactor authorization handlers
romandykyi Dec 26, 2023
a9477f5
Update comments
romandykyi Dec 26, 2023
2188767
Refactor `AssignedGradesFilter`
romandykyi Dec 26, 2023
fa0336b
Add an additional method for `GroupsService`
romandykyi Dec 26, 2023
b876a6b
Remove unnecessary code
romandykyi Dec 26, 2023
64c3add
Add `Async` suffix
romandykyi Dec 26, 2023
97afc7a
Forbid students view another students via groups or semesters
romandykyi Dec 26, 2023
c7c79e1
Implement some assigned grades get methods
romandykyi Dec 26, 2023
bde1ca9
Implement `IAssignedGradesService.GetAssignerIdAsync`
romandykyi Dec 26, 2023
5c31126
Remove redundant includes
romandykyi Dec 26, 2023
2ed6766
Update return type
romandykyi Dec 26, 2023
3b230b2
Fix dto property name
romandykyi Dec 26, 2023
c5f211a
Implement controller for assigned grades
romandykyi Dec 26, 2023
beeeda5
Fix false negatives in tests
romandykyi Dec 26, 2023
8f1e0f9
Fix accidental change
romandykyi Dec 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Refactor authorization handlers
Renamed `ViewStudentEnrollmentsAuthorizationHandler` into `AccessOnlyOwnDataAuthorizationHandler` and made it more modular.
  • Loading branch information
romandykyi committed Dec 26, 2023
commit f0d17ed0b35489a6ca0d21fe6768ac15c3ce44f1
5 changes: 5 additions & 0 deletions Core/Policy/Policies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@ public static class Policies
public const string IsTeacher = "IsTeacher";
public const string IsTeacherOrAdministrator = "IsTeacherOrAdmin";
public const string HasAdministratorPermission = "HasAdministratorPermission";
/// <summary>
/// Policy that determines whether user can view students enrollments.
/// Allows teachers and administrators to view all enrollments and other users to view
/// only their own enrollments.
/// </summary>
public const string CanViewStudentEnrollments = "CanViewStudentEnrollments";
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@

namespace EUniversity.Tests.Auth;

public class ViewStudentEnrollmentsAuthorizationHandlerTests
public class AccessOnlyOwnDataAuthorizationHandlerTests
{
private readonly string TestUserId = Guid.NewGuid().ToString();
private readonly string TestRouteStudentId = Guid.NewGuid().ToString();

private ClaimsPrincipal GetUser(string id, params string[] roles)
{
List<Claim> claims =new()
List<Claim> claims = new()
{
new(JwtClaimTypes.Subject, id)
};
Expand All @@ -29,7 +29,7 @@ private ClaimsPrincipal GetUser(string id, params string[] roles)
return new ClaimsPrincipal(identity);
}

private AuthorizationHandlerContext GetHandlerContext(ClaimsPrincipal user)
private AuthorizationHandlerContext GetHandlerContext(ClaimsPrincipal user, params string[] roles)
{
RouteValueDictionary routeValues = new()
{
Expand All @@ -43,7 +43,7 @@ private AuthorizationHandlerContext GetHandlerContext(ClaimsPrincipal user)

IAuthorizationRequirement[] requirements =
{
new ViewStudentEnrollmentsAuthorizationRequirement()
new AccessOnlyOwnDataAuthorizationRequirement(roles)
};

return new(requirements, user, httpContext);
Expand All @@ -52,12 +52,12 @@ private AuthorizationHandlerContext GetHandlerContext(ClaimsPrincipal user)
[Test]
[TestCase(Roles.Administrator)]
[TestCase(Roles.Teacher)]
public async Task AdministratorOrTeacher_Succeeds(string role)
public async Task SkipRoleAccessesNotOwnData_Succeeds(string role)
{
// Arrange
ClaimsPrincipal user = GetUser(TestUserId, role);
AuthorizationHandlerContext context = GetHandlerContext(user);
ViewStudentEnrollmentsAuthorizationHandler handler = new();
AuthorizationHandlerContext context = GetHandlerContext(user, Roles.Administrator, Roles.Teacher);
AccessOnlyOwnDataAuthorizationHandler handler = new();

// Act
await handler.HandleAsync(context);
Expand All @@ -67,12 +67,12 @@ public async Task AdministratorOrTeacher_Succeeds(string role)
}

[Test]
public async Task UserAccessesOwnEnrollments_Succeeds()
public async Task UserAccessesOwnData_Succeeds()
{
// Arrange
ClaimsPrincipal user = GetUser(TestRouteStudentId);
AuthorizationHandlerContext context = GetHandlerContext(user);
ViewStudentEnrollmentsAuthorizationHandler handler = new();
AccessOnlyOwnDataAuthorizationHandler handler = new();

// Act
await handler.HandleAsync(context);
Expand All @@ -82,12 +82,12 @@ public async Task UserAccessesOwnEnrollments_Succeeds()
}

[Test]
public async Task UserAccessesEnrollmentsOfAnotherUser_Fails()
public async Task UserAccessesDataOfAnotherUser_Fails()
{
// Arrange
ClaimsPrincipal user = GetUser(TestUserId);
AuthorizationHandlerContext context = GetHandlerContext(user);
ViewStudentEnrollmentsAuthorizationHandler handler = new();
AccessOnlyOwnDataAuthorizationHandler handler = new();

// Act
await handler.HandleAsync(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,22 @@
namespace EUniversity.Auth;

/// <summary>
/// Authorization handler that determines whether user can view students enrollments.
/// Allows teachers and administrators to view all enrollments and other users to view
/// only their own enrollments.
/// Authorization handler that allows users only access their own data.
/// <see cref="AccessOnlyOwnDataAuthorizationRequirement" /> can contain roles
/// that are allowed to access any data.
/// </summary>
public class ViewStudentEnrollmentsAuthorizationHandler :
AuthorizationHandler<ViewStudentEnrollmentsAuthorizationRequirement>
public class AccessOnlyOwnDataAuthorizationHandler :
AuthorizationHandler<AccessOnlyOwnDataAuthorizationRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ViewStudentEnrollmentsAuthorizationRequirement requirement)
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AccessOnlyOwnDataAuthorizationRequirement requirement)
{
if (!context.User.IsAuthenticated())
{
context.Fail();
return Task.CompletedTask;
}
// If user is either a teacher or an administrator,
// he/she has an access to all enrollments
if (context.User.HasClaim(JwtClaimTypes.Role, Roles.Teacher) ||
context.User.HasClaim(JwtClaimTypes.Role, Roles.Administrator))
// Check if we can skip the check
if (requirement.SkipRoles.Any(r => context.User.HasClaim(JwtClaimTypes.Role, r)))
{
context.Succeed(requirement);
return Task.CompletedTask;
Expand Down
30 changes: 30 additions & 0 deletions EUniversity/Auth/AccessOnlyOwnDataAuthorizationRequirement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.AspNetCore.Authorization;

namespace EUniversity.Auth;

/// <summary>
/// Requirement for <see cref="AccessOnlyOwnDataAuthorizationHandler" />.
/// </summary>
public class AccessOnlyOwnDataAuthorizationRequirement : IAuthorizationRequirement
{
/// <summary>
/// Gets roles that are allowed to access any data.
/// </summary>
public IEnumerable<string> SkipRoles { get; }

public AccessOnlyOwnDataAuthorizationRequirement() : this(Enumerable.Empty<string>())
{

}

public AccessOnlyOwnDataAuthorizationRequirement(params string[] skipRoles) :
this(skipRoles.AsEnumerable())
{

}

public AccessOnlyOwnDataAuthorizationRequirement(IEnumerable<string> skipRoles)
{
SkipRoles = skipRoles;
}
}

This file was deleted.

4 changes: 2 additions & 2 deletions EUniversity/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public static IServiceCollection AddCustomizedIdentity(this IServiceCollection s

public static IServiceCollection AddCustomizedAuthorization(this IServiceCollection services, params string[] authenticationSchemes)
{
services.AddTransient<IAuthorizationHandler, ViewStudentEnrollmentsAuthorizationHandler>();
services.AddTransient<IAuthorizationHandler, AccessOnlyOwnDataAuthorizationHandler>();

return services.AddAuthorization(options =>
{
Expand Down Expand Up @@ -138,7 +138,7 @@ public static IServiceCollection AddCustomizedAuthorization(this IServiceCollect
{
policy.AddAuthenticationSchemes(authenticationSchemes);
policy.RequireAuthenticatedUser();
policy.AddRequirements(new ViewStudentEnrollmentsAuthorizationRequirement());
policy.AddRequirements(new AccessOnlyOwnDataAuthorizationRequirement(Roles.Teacher, Roles.Administrator));
});
options.DefaultPolicy = options.GetPolicy(Policies.Default)!;
});
Expand Down