I am trying to replace our own Audit system with Audit.NET. I have checked the documentation for Audit.EntityFramework, https://github.com/thepirat000/Audit.NET/tree/master/src/Audit.EntityFramework, and it is not clear to me where the configuration setup should be added. Also, lets say I am building a ASP.NET CORE RestFUL API and need to keep track of users making changes by extracting user information from a JWT, how would I set that up with Audit.EntityFramework?
In the documentation there is the follow code snippet to configure audits for orders and tracking users :
Audit.Core.Configuration.Setup()
.UseEntityFramework(ef => ef
.AuditTypeExplicitMapper(m => m
.Map<Order, Audit_Order>()
.Map<OrderItem, Audit_OrderItem>()
.AuditEntityAction<IAudit>((evt, entry, auditEntity) =>
{
auditEntity.AuditDate = DateTime.UtcNow;
auditEntity.UserName = evt.Environment.UserName;
auditEntity.AuditAction = entry.Action; // Insert, Update, Delete
})
)
);
However, if I would add that in the Startup.cs it would not help track what each user is doing on each call made by different users on the different endpoints. Do you have any example of how I can do this using Audit.EntityFramework?
Thanks
Have you seen the main Audit.NET documentation?
Yes, the setup must be executed before any audit takes place, so on your start-up code should be fine.
If you need to add more information to the audit events you could use custom actions that also should be setup on your start-up code. That doesn't mean you have to set the value at the start-up, but you have to provide a way to get the value on the start-up. For example if you need something from the current HttpContext, you could get it from an HttpContextAccessor. For example:
public void Configure(IApplicationBuilder app, IHttpContextAccessor contextAccessor)
{
// ...
Audit.Core.Configuration.AddCustomAction(ActionType.OnScopeCreated, scope =>
{
var httpContext = contextAccessor.HttpContext;
// Add a new field
scope.Event.CustomFields["CorrelationId"] = httpContext.TraceIdentifier;
// reuse an existing field
scope.Event.Environment.UserName = GetUserFromContext(httpContext);
});
}
Also, Audit.NET provides two libraries Audit.WebApi and Audit.MVC to audit Web API and/or MVC calls.
Also there is a dotnet new template exposed you could use to quickly create a minimal WebAPI/MVC project with audit enabled and using entity framework. For example:
dotnet new -i Audit.WebApi.Template
dotnet new webapiaudit -E
Related
I am working on an azure function which is a part of a system. The authentication/ authorization of system is controlled by OpenIdDict library. After using our system for sometime in our production, there are millions of invalid and expired tokens in the OpenIddictTokens table which I believe is causing some of the calls in our system to slowdown.
Now I am working on a time triggered azure function whose purpose is to delete (get rid of) all the useless tokens & authorizations saved in the OpenIddictTokens and OpenIddictAuthorizations tables respectively.
I started looking at the openiddict documentation and api but could not find the exact match for my requirements related to implementation in azure yet.
Can someone please help? Thanks.
After looking into the documentation and experimenting with code, I was able to find the method and how to use this in my azure functions app.
First add the dependency for openiddict in startup:
builder.Services.AddOpenIddict()
// Register the OpenIddict core services.
.AddCore(options =>
{
// Register the Entity Framework stores and models.
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>();
});
Then create the respective function with the IOpenIddictAuthorizationManager and IOpenIddictTokenManager as dependencies and call the PruneAsync method for both.
private readonly IOpenIddictAuthorizationManager _openIddictAuthorizationManager;
private readonly IOpenIddictTokenManager _openIddictTokenManager;
public PruneTokenFunction(IOpenIddictAuthorizationManager openIddictAuthorizationManager, IOpenIddictTokenManager openIddictTokenManager)
{
_openIddictAuthorizationManager = openIddictAuthorizationManager;
_openIddictTokenManager = openIddictTokenManager;
}
[FunctionName("prunetoken")]
public async Task Run([TimerTrigger("0 */5 * * * ")] TimerInfo timerInfo)
{
await _openIddictTokenManager.PruneAsync(DateTimeOffset.Now.AddDays(-1));
await _openIddictAuthorizationManager.PruneAsync(DateTimeOffset.Now.AddDays(-1));
}
Also following is the issue related to same query which might be helpful to many. Implement automatic expired token flushing
I am using GraphQL .net to respond to graphql queries on the backend of an Asp.net Core website. All the cookies seem to be passed with the requests but for some reason my graphql.net requests do not have the proper user session set on the HttpContext. The ClaimPrincipal is mostly empty via graphql.net while my Asp.net Core WebApi/Mvc style endpoints have the correct principal with user id even though both GraphQl.Net requests and non-graphql.net requests are happening at the same time.
I checked the payload and all the same cookies are passed in both requests. So it makes me wonder why are my regular WebApi endpoints able to (auto-magically) get the claims principal and why can't the graph.net endpoints do the same. As far as I know from previous usages of GraphQl.net I wasn't aware that any special session code had to be added (other than passing the user from the HttpContext to graphQL.net).
I've been reading through GraphQL.Net and Asp.net core source code and docs, but so far I haven't found any obvious offenses or leads.
What might cause some issue like this? what are some common causes?
Should I just try to figure out how to manually read in the cookie to Asp.net core and pull the principal?
Perhaps I'm missing a special header value? I don't think the headers are weird but I haven't done a side by side comparison between the headers in graphql.net and asp.net core requests.
In this snippet is where I first detect a problem. If I put a breakpoint here then the claimsprinical isn't correctly set for the current user session. And also later when I access the HttpContext the user session is not correct for graphql.net requests.
public static GraphQLUserContext InitializeFromContext(HttpContext httpContext)
{
return new GraphQLUserContext
{
User = httpContext.User,
};
}
Here's part of the Graphql.net configuration:
services.AddGraphQL((options, provider) =>
{
options.EnableMetrics = _env.IsDevelopment();
var logger = provider.GetRequiredService<ILogger<WebDependencyInjectionConfig>>();
options.UnhandledExceptionDelegate = ctx => logger.LogError("{Error} occurred", ctx.OriginalException.Message);
})
.AddErrorInfoProvider(opt =>
{
opt.ExposeExceptionStackTrace = _env.IsDevelopment();
opt.ExposeCodes = _env.IsDevelopment();
opt.ExposeCode = _env.IsDevelopment();
opt.ExposeData = _env.IsDevelopment();
opt.ExposeExtensions = _env.IsDevelopment();
})
.AddSystemTextJson()
.AddUserContextBuilder(GraphQLUserContext.InitializeFromContext)
.AddGraphTypes(typeof(PrimarySchema), ServiceLifetime.Scoped);
I'll gladly provide any requested configuration if anyone wants it, but there is a lot of possible code it touches. Thanks!
What does your Configure method look like? Is your app.UseAuthentication() before your GraphQL middleware configuration?
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseAuthorization();
app.UseGraphQL<MySchema>();
}
https://github.com/dotnet/aspnetcore/blob/790c4dc2cf59e16e6144f7790328d563ca310533/src/Security/samples/Cookies/Startup.cs#L45-L66
I am setting up multiple graph mappings to an OrientDB database in Gremlin server. However, I can't find what to script in Groovy plus what to configure in the configuration yaml file in order to be able to map each authenticated user to a single graph, rather than have all the users validated by the authenticator be able to access everything. Is there any way to achieve this?
Gremlin Server does not provide any features for authorization - only authentication. You would have to build something yourself to handle restricting users to different graphs (or other constraints). That would mean building two things:
A custom ChannelInboundHandlerAdapter to handle authorization - maybe called AuthorizationHandler
A custom Channelizer implementation to wire in your custom authorizer to the pipeline - maybe called AuthorizingChannelizer
The AuthorizationHandler would basically just override Netty's channelRead() method
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof RequestMessage){
RequestMessage requestMessage = (RequestMessage) msg;
// examine contents of RequestMessage to see what is being requested
// e.g. the graph - the user information will be there too but
// depending on the authentication method you're using you might need
// to re-decode it at this time as it doesn't appear that the authenticated
// user is placed on the ChannelHandlerContext for some reason. i made
// a note to change that as it seems helpful and is a simple enough thing
// to do
}
}
For the AuthorizingChannelizer you would basically extend the WebSocketChannelizer and override the configure() method:
#Override
public void configure(ChannelPipeline pipeline) {
super.configure(pipeline);
// add an instance of your `AuthorizingChannelizer` to the end of the
// netty pipeline which will put it after the `AuthenticationHandler`
// but before all the Gremlin processing/execution
pipeline.addLast("authorizier", authorizingChannelizer);
}
Then, in your Gremlin Server config you replace the channelizer setting with the fully qualified name of your AuthorizingChannelizer. Assuming you've placed your jar containing that class in Gremlin Server's path it should create an instance of it at startup.
I would look at the existing "handler" and "channelizer" code for more inspiration on how to make this happen.
In my index I added a search field.
When user enter a search term and click filter the index (Index) is filtered.
So far so good.
What I would like to achieve is that if the user performs other operations (edit, details, delete, etc) in the same controller and returns to the Index, I would like the search to be restored.
To do this, I used TempData but without success.
In the various forums / tutorials I found conflicting about lifetime.
Some say:
lifetime of an object placed in TempData is exactly one additional
request.
See this stackoverflow article
On another site i found:
Data stored in TempData will be alive in the cookie until you read it
from the TempData dictionary.
See this article
So where is the truth:
Only one sub request or when I read the TempData ?
My tests says the second: "until you read it" (or when expire)
Here my code on startup
public void ConfigureServices(IServiceCollection services)
{
// [..]
// Relevant code only
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromMinutes(15);
options.CookieHttpOnly = true;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// [..]
// Relevant code only
app.UseSession();
}
On my controller
public async Task<IActionResult> Index(int page, string search)
{
search = search ?? TempData["Search"]?.ToString();
// Query with search data
TempData["search"] = search;
}
If I search on this controller the TempData save the search term.
If, after searching in the list, I navigate to other pages and then return here, the search term is still present
I already know that exist .Keep, .Peek and other methods for manage the TempData
Questions
How manage the search term between actions ?
How work the TempData (until re-read or on ONE addictional request) ?
Judging by many older Stack Overflow posts and articles out there, it seems TempData only lasted until the next request in prior versions of ASP.NET. However, that is not the case as of .NET Core according to Microsoft:
ASP.NET Core exposes the Razor Pages TempData or Controller TempData. This property stores data until it's read in another request. The Keep(String) and Peek(string) methods can be used to examine the data without deletion at the end of the request. Keep marks all items in the dictionary for retention.
Another claim I see repeatedly about TempData is how it is implemented using sessions, but that isn't the default mechanism as of .NET Core:
The cookie-based TempData provider is enabled by default. To enable the session-based TempData provider, use the AddSessionStateTempDataProvider extension method.
Source:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-3.1#tempdata
I'm writing a silverlight application that resembles a shopping cart system.
This app can only be launched from the asp.net website after the user is logged in to the site.
Upon first load, the app will send a request to the backend through WCF service to retreive all the existing items in the shopping cart.
Therefore the silverlight app needs to know who the current user is, to find which shopping cart to load.
I found there are a couple of ways so far, but not happy with any of them:
using wcf aspnet compat. silverlight can ask who the current user is by asking the wcf service.
pass parameters from the page to xaml by using xaml.InitParameters and pass in the minimum amount of information to identify a user in a serialized format.
pass parameters through query string to xaml (apparently this is also possible)
Can anyone share the best practice to achieve this?
Thanks
We use the first solution in our projects. You haven't to invent any type of serialization format or so in this case. A disadvantage of this approach - extra async logic at startup.
The example of service:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class UserInfoService : IUserInfoService
{
public UserInfo GetUserInfo()
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
return null;
var userInfo = new UserInfo
{
Login = HttpContext.Current.User.Identity.Name,
Fullname = ...,
};
return userInfo;
}
}
Sending userid via initParams or query string is not good idea I think. Such things should be more hidden.
The real important thing is to verify user on server on each service call because anyone can call your services in similar way as your app.
HTH