Skip to content

Commit a8ff1f8

Browse files
authored
Merge pull request #82 from Virtual-Finland-Development/feat/db-auditlogs
Audit logs using postgresql triggers
2 parents 124ddcc + 9b88217 commit a8ff1f8

81 files changed

Lines changed: 1941 additions & 814 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Docs/README.adminfunction.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ dotnet run --project ./VirtualFinland.UsersAPI.AdminFunction.CLI migrate
3333
- `migrate` - runs the database migrations
3434
- lambda function payload: `{"Action": "Migrate"}`
3535
- cli command: `dotnet run --project ./VirtualFinland.UsersAPI.AdminFunction.CLI migrate`
36+
- `initialize-database-audit-log-triggers` - initializes the database audit logging triggers
37+
- lambda function payload: `{"Action": "InitializeDatabaseAuditLogTriggers"}`
38+
- cli command: `dotnet run --project ./VirtualFinland.UsersAPI.AdminFunction.CLI initialize-database-audit-log-triggers`
3639
- `initialize-database-user` - setup the application-level user credentials to the database
3740
- lambda function payload: `{"Action": "InitializeDatabaseUser", "data": "{\"Username\": \"appuser\", \"Password\": \"pass\"}"}`
3841
- cli command: `DATABASE_USER=appuser DATABASE_PASSWORD=pass dotnet run --project ./VirtualFinland.UsersAPI.AdminFunction.CLI initialize-database-user`

Docs/README.deployment.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,12 @@ Deployment to a fresh environment requires the following steps to be performed i
2020
- Select the tab "Test"
2121
- Add a new test event with the following content: `{"Action": "Migrate"}`
2222
- Click "Test" button and wait for the execution to finish
23+
24+
## Deployment on existing environment
25+
26+
Pulumi does not have good tooling to handle attaching to existing resources such as AWS CloudWatch log groups. When deploying to a system where there are already resources created, one could tackle the problem by manually importing the resources to the pulumi state.
27+
28+
Example for importing a CloudWatch log group of a RDS instance:
29+
- `pulumi import aws:cloudwatch/logGroup:LogGroup users-api-database-dev /aws/rds/instance/users-api-postgres-db-dev2313s/postgresql`
30+
31+
Read more of the pulumi import-tooling from [here](https://www.pulumi.com/docs/cli/commands/pulumi_import/).

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ packages: test
2525
dotnet lambda package --project-location ./VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI --output-package ./build-artifacts/VirtualFinland.UsersAPI.zip
2626
@echo "> Building and packaging deployment package for the admin functions"
2727
dotnet lambda package --project-location ./VirtualFinland.UsersAPI.AdminFunction --output-package ./build-artifacts/VirtualFinland.UsersAPI.AdminFunction.zip
28+
@echo "> Building and packaging deployment package for the audit log subscription function"
29+
dotnet lambda package --project-location ./VirtualFinland.UsersAPI.AuditLogSubscription --output-package ./build-artifacts/VirtualFinland.UsersAPI.AuditLogSubscription.zip
2830

2931
deploy: packages
3032
pulumi -C ./VirtualFinland.UsersAPI.Deployment up

VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI/Activities/Identity/IdentityController.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
using System.Security.Claims;
21
using MediatR;
32
using Microsoft.AspNetCore.Authorization;
43
using Microsoft.AspNetCore.Mvc;
54
using Swashbuckle.AspNetCore.Annotations;
65
using VirtualFinland.UserAPI.Activities.Identity.Operations;
7-
using VirtualFinland.UserAPI.Helpers;
86

97
namespace VirtualFinland.UserAPI.Activities.Identity;
108

@@ -25,15 +23,13 @@ public IdentityController(IMediator mediator)
2523
[SwaggerOperation(Summary = "Verifies the existence of a user that was identified by an external identity provider.",
2624
Description =
2725
"Given the access token from an external identity provider, the operation tries to find if the user exists in the system database and creates the user into the system. Notice: The user can't access the API other paths without being created into the system with this call.")]
28-
[ProducesResponseType(typeof(VerifyIdentityUser.User), StatusCodes.Status200OK)]
26+
[ProducesResponseType(typeof(VerifyIdentityPerson.User), StatusCodes.Status200OK)]
2927
[ProducesErrorResponseType(typeof(ProblemDetails))]
30-
public async Task<IActionResult> VerifyIdentityUser()
28+
public async Task<IActionResult> VerifyIdentityPerson()
3129
{
3230
var user = await _mediator.Send(
33-
new VerifyIdentityUser.Query(
34-
this.User.FindFirst(ClaimTypes.NameIdentifier)?.Value ??
35-
this.User.FindFirst(Constants.Web.ClaimUserId)?.Value,
36-
this.User.Claims.First().Issuer
31+
new VerifyIdentityPerson.Query(
32+
HttpContext
3733
)
3834
);
3935

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using MediatR;
2+
using Swashbuckle.AspNetCore.Annotations;
3+
using VirtualFinland.UserAPI.Helpers.Extensions;
4+
using VirtualFinland.UserAPI.Helpers.Services;
5+
using VirtualFinland.UserAPI.Security.Models;
6+
7+
namespace VirtualFinland.UserAPI.Activities.Identity.Operations;
8+
9+
public static class VerifyIdentityPerson
10+
{
11+
[SwaggerSchema(Title = "TestbedIdentityUserRequest")]
12+
public class Query : IRequest<User>
13+
{
14+
public Query(HttpContext context)
15+
{
16+
Context = context;
17+
}
18+
19+
public HttpContext Context { get; }
20+
}
21+
22+
public class Handler : IRequestHandler<Query, User>
23+
{
24+
private readonly AuthenticationService _authenticationService;
25+
private readonly ILogger<Handler> _logger;
26+
27+
public Handler(AuthenticationService authenticationService, ILogger<Handler> logger)
28+
{
29+
_authenticationService = authenticationService;
30+
_logger = logger;
31+
}
32+
33+
public async Task<User> Handle(Query request, CancellationToken cancellationToken)
34+
{
35+
var person = await _authenticationService.AuthenticateAndGetOrRegisterAndGetPerson(request.Context, cancellationToken);
36+
_logger.LogAuditLogEvent(AuditLogEvent.Read, "Identity", request.Context.Items["User"] as RequestAuthenticatedUser ?? throw new Exception("Unknown error occurred on verifying identity"));
37+
return new User(person.Id, person.Created, person.Modified);
38+
}
39+
}
40+
41+
[SwaggerSchema(Title = "TestbedIdentityUserResponse")]
42+
public record User(Guid Id, DateTime Created, DateTime Modified);
43+
}

VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI/Activities/Identity/Operations/VerifyIdentityUser.cs

Lines changed: 0 additions & 77 deletions
This file was deleted.

VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI/Activities/Productizer/Operations/BasicInformation/GetPersonBasicInformation.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,38 @@
22
using Microsoft.EntityFrameworkCore;
33
using Swashbuckle.AspNetCore.Annotations;
44
using VirtualFinland.UserAPI.Data;
5-
using VirtualFinland.UserAPI.Helpers.Swagger;
5+
using VirtualFinland.UserAPI.Helpers;
6+
using VirtualFinland.UserAPI.Helpers.Extensions;
7+
using VirtualFinland.UserAPI.Security.Models;
68

79
namespace VirtualFinland.UserAPI.Activities.Productizer.Operations.BasicInformation;
810

911
public static class GetPersonBasicInformation
1012
{
1113
[SwaggerSchema(Title = "GetPersonBasicInformationRequest")]
12-
public class Query : IRequest<GetPersonBasicInformationResponse>
14+
public class Query : AuthenticatedRequest<GetPersonBasicInformationResponse>
1315
{
14-
public Query(Guid? userId)
16+
public Query(RequestAuthenticatedUser RequestAuthenticatedUser) : base(RequestAuthenticatedUser)
1517
{
16-
UserId = userId;
1718
}
18-
19-
[SwaggerIgnore]
20-
public Guid? UserId { get; }
2119
}
2220

2321
public class Handler : IRequestHandler<Query, GetPersonBasicInformationResponse>
2422
{
2523
private readonly UsersDbContext _context;
24+
private readonly ILogger<Handler> _logger;
2625

27-
public Handler(UsersDbContext context)
26+
public Handler(UsersDbContext context, ILogger<Handler> logger)
2827
{
2928
_context = context;
29+
_logger = logger;
3030
}
3131

3232
public async Task<GetPersonBasicInformationResponse> Handle(Query request, CancellationToken cancellationToken)
3333
{
34-
var person = await _context.Persons.SingleAsync(p => p.Id == request.UserId, cancellationToken);
34+
var person = await _context.Persons.SingleAsync(p => p.Id == request.User.PersonId, cancellationToken);
35+
36+
_logger.LogAuditLogEvent(AuditLogEvent.Read, "Person", request.User);
3537

3638
return new GetPersonBasicInformationResponse(
3739
person.GivenName,

VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI/Activities/Productizer/Operations/BasicInformation/UpdatePersonBasicInformation.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
using Swashbuckle.AspNetCore.Annotations;
44
using VirtualFinland.UserAPI.Data;
55
using VirtualFinland.UserAPI.Exceptions;
6-
using VirtualFinland.UserAPI.Helpers.Swagger;
6+
using VirtualFinland.UserAPI.Helpers;
7+
using VirtualFinland.UserAPI.Helpers.Extensions;
8+
using VirtualFinland.UserAPI.Security.Models;
79

810
namespace VirtualFinland.UserAPI.Activities.Productizer.Operations.BasicInformation;
911

1012
public static class UpdatePersonBasicInformation
1113
{
12-
public class Command : IRequest<UpdatePersonBasicInformationResponse>
14+
public class Command : AuthenticatedRequest<UpdatePersonBasicInformationResponse>
1315
{
1416
public Command(string? givenName, string? lastName, string email, string? phoneNumber, string residency)
1517
{
@@ -20,34 +22,28 @@ public Command(string? givenName, string? lastName, string email, string? phoneN
2022
Residency = residency;
2123
}
2224

23-
[SwaggerIgnore]
24-
public Guid? UserId { get; set; }
25-
2625
public string? GivenName { get; }
2726
public string? LastName { get; }
2827
public string Email { get; }
2928
public string? PhoneNumber { get; }
3029
public string? Residency { get; }
31-
32-
public void SetAuth(Guid? userDatabaseId)
33-
{
34-
UserId = userDatabaseId;
35-
}
3630
}
3731

3832
public class Handler : IRequestHandler<Command, UpdatePersonBasicInformationResponse>
3933
{
4034
private readonly UsersDbContext _context;
35+
private readonly ILogger<Handler> _logger;
4136

42-
public Handler(UsersDbContext context)
37+
public Handler(UsersDbContext context, ILogger<Handler> logger)
4338
{
4439
_context = context;
40+
_logger = logger;
4541
}
4642

4743
public async Task<UpdatePersonBasicInformationResponse> Handle(Command request,
4844
CancellationToken cancellationToken)
4945
{
50-
var person = await _context.Persons.SingleAsync(p => p.Id == request.UserId, cancellationToken);
46+
var person = await _context.Persons.SingleAsync(p => p.Id == request.User.PersonId, cancellationToken);
5147

5248
person.GivenName = request.GivenName ?? person.GivenName;
5349
person.LastName = request.LastName ?? person.LastName;
@@ -57,13 +53,14 @@ public async Task<UpdatePersonBasicInformationResponse> Handle(Command request,
5753

5854
try
5955
{
60-
await _context.SaveChangesAsync(cancellationToken);
56+
await _context.SaveChangesAsync(request.User, cancellationToken);
6157
}
6258
catch (DbUpdateException e)
6359
{
6460
throw new BadRequestException(e.InnerException?.Message ?? e.Message);
6561
}
6662

63+
_logger.LogAuditLogEvent(AuditLogEvent.Update, "Person", request.User);
6764

6865
return new UpdatePersonBasicInformationResponse
6966
(

VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI/Activities/Productizer/Operations/JobApplicantProfile/GetJobApplicantProfile.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,29 @@
44
using Swashbuckle.AspNetCore.Annotations;
55
using VirtualFinland.UserAPI.Data;
66
using VirtualFinland.UserAPI.Helpers;
7-
using VirtualFinland.UserAPI.Helpers.Swagger;
7+
using VirtualFinland.UserAPI.Helpers.Extensions;
8+
using VirtualFinland.UserAPI.Security.Models;
89

910
namespace VirtualFinland.UserAPI.Activities.Productizer.Operations.JobApplicantProfile;
1011

1112
public static class GetJobApplicantProfile
1213
{
13-
public class Query : IRequest<PersonJobApplicantProfileResponse>
14+
public class Query : AuthenticatedRequest<PersonJobApplicantProfileResponse>
1415
{
15-
public Query(Guid? personId)
16+
public Query(RequestAuthenticatedUser RequestAuthenticatedUser) : base(RequestAuthenticatedUser)
1617
{
17-
PersonId = personId;
1818
}
19-
20-
[SwaggerIgnore]
21-
public Guid? PersonId { get; }
2219
}
2320

2421
public class Handler : IRequestHandler<Query, PersonJobApplicantProfileResponse>
2522
{
2623
private readonly UsersDbContext _context;
24+
private readonly ILogger<Handler> _logger;
2725

28-
public Handler(UsersDbContext context)
26+
public Handler(UsersDbContext context, ILogger<Handler> logger)
2927
{
3028
_context = context;
29+
_logger = logger;
3130
}
3231

3332
public async Task<PersonJobApplicantProfileResponse> Handle(Query request, CancellationToken cancellationToken)
@@ -40,7 +39,9 @@ public async Task<PersonJobApplicantProfileResponse> Handle(Query request, Cance
4039
.Include(p => p.Certifications)
4140
.Include(p => p.Permits)
4241
.Include(p => p.WorkPreferences)
43-
.SingleAsync(p => p.Id == request.PersonId, cancellationToken);
42+
.SingleAsync(p => p.Id == request.User.PersonId, cancellationToken);
43+
44+
_logger.LogAuditLogEvent(AuditLogEvent.Read, "JobApplicantProfile", request.User);
4445

4546
return new PersonJobApplicantProfileResponse
4647
{

0 commit comments

Comments
 (0)