I've seen this discussed a few times, and the best solution I've seen has been to implement a new Hangfire Dashboard Authorization Filter:
1.) in Startup.cs, change the Hangfire Dashboard Authorization option to the following:
options.Authorization = new[] { new HangfireTokenAuthorizationFilter(AppPermissions.Pages_Administration_HangfireDashboard) };
2.) somewhere in your solution, mine is in my .Web.Host project, I have defined this class:
public class HangfireTokenAuthorizationFilter : IDashboardAuthorizationFilter
{
private readonly string _requiredPermissionName;
public HangfireTokenAuthorizationFilter(string requiredPermissionName = null)
{
_requiredPermissionName = requiredPermissionName;
}
public bool Authorize([NotNull]DashboardContext context)
{
var logger = context.GetHttpContext().RequestServices.GetRequiredService<ILogger>();
// if we have a cookies and we are in release mode
var cookies = context.GetHttpContext().Request.Cookies;
if (cookies["Abp.AuthToken"] != null)
{
var jwtCookie = cookies["Abp.AuthToken"];
string jwtToken = jwtCookie;
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
JwtSecurityToken securityToken = handler.ReadToken(jwtToken) as JwtSecurityToken;
var id = securityToken.Claims.FirstOrDefault(claim => claim.Type.EndsWith("sub"));
var tenanIdClaim = securityToken.Claims.FirstOrDefault(claim => claim.Type.EndsWith("tenantId"));
int? tenanId = null;
if (tenanIdClaim != null)
{
tenanId = int.Parse(tenanIdClaim.Value);
}
logger.InfoFormat("[HangfireTokenAuthorizationFilter.Authorize]: checking Permissions: permission: '{0}', tenant: '{1}', user_id: '{2}'", _requiredPermissionName, tenanId, id.Value);
var isPermissionGranted = IsPermissionGranted(context, _requiredPermissionName, new UserIdentifier(tenanId, long.Parse(id.Value)));
if(!isPermissionGranted)
{
logger.WarnFormat("[HangfireTokenAuthorizationFilter.Authorize]: Unauthorized Attempt to access the Hangfire Dashboard: tenant: '{0}', user_id: '{1}'", _requiredPermissionName, tenanId, id.Value);
}
return isPermissionGranted;
}
logger.Warn("[HangfireTokenAuthorizationFilter.Authorize]: no Abp.AuthToken cookie present.");
return false;
}
private static bool IsPermissionGranted(DashboardContext context, string requiredPermissionName, UserIdentifier userIdentifier)
{
var permissionChecker = context.GetHttpContext().RequestServices.GetRequiredService<IPermissionChecker>();
return permissionChecker.IsGranted(userIdentifier, requiredPermissionName);
}
}
3.) in your .Core project, under Authorization, in the AppPermissions.cs and AppAuthorizationProvider.cs classes, make sure that you have lines that define the Hangfire Dashboard permission
administration.CreateChildPermission(AppPermissions.Pages_Administration_HangfireDashboard, L("HangfireDashboard"), multiTenancySides: _isMultiTenancyEnabled ? MultiTenancySides.Host : MultiTenancySides.Tenant);
and
public const string Pages_Administration_HangfireDashboard = "Pages.Administration.HangfireDashboard";
At this point, when you setup role permissions through the .Web.Host UI, you should see Hangfire Dashboard listed as a permission-controlled item.
Then the last step is you need to add navigation to the Hangfire Dashboard to your Admin UI.
4.) In the .Web.Host project, under src\app\shared\layout\nav\app-navigation.service.ts, you need to add:
import { AppConsts } from '@shared/AppConsts';
~line 5 in the import statements @ the top
new AppMenuItem('HangfireDashboard', 'Pages.Administration.HangfireDashboard', 'flaticon-line-graph', AppConsts.remoteServiceBaseUrl + '/hangfire', [], true),
somewhere under your Administration AppMenuItem definition.
once you have that you should now see Hangfire Dashboard in your Administration menu, and if you click on it, it should open a new tab/window to the Hangfire Dashboard using the correct authorization filter/scheme that adheres to your ASPNZ permissions model.
thank you!
Has anyone tried using Swagger to split up their API specification into multiple files?
I have a case where I want to have 1 "private" file for the internal application, and then a 2nd "public" file that exposes only public/anonymous endpoints I have done this before with Swashbuckle annotations & classic .NET WebAPI, but never with ASPNZ or ABP.
thanks!
A question about tenancy identification and domains. Has anyone setup multi-tenancy with identitying each tenant by a unique fully qualified domain, rather than by a subdomain?
I’m building a system where the host would be “app.foo.com”, but then each individual tenant would be “foo.<companyname>.com”. Yes it requires correct dns configuration by each tenants dns, but I was wondering how internally the aspnz framework would handle that, or where I would look to potentially customize that.
thanks, -Brian
i am working on a mobile app that needs to support offline mode and i am currently running into an issue with the Flurl.Http client.
So what happen is if you are online, switch to offline and it works fine. But when you switch online again from offline, the Flur client throws a timeout exeception.
Here is a similar issue that I found: https://github.com/tmenier/Flurl/issues/352
has anyone else seen this before? Should i look to swotch from Flurl to the .net native HttpClient?
Thanks!
per feedback from another developer on the Slack channel (yeah Slack!), he directed me into using this library:
https://github.com/jamesmontemagno/monkey-cache
Hi,
I am working on a Xamarin mobile app that needs to work offline. However, I am dealing with a configuration issue. Basically it doesn't work with offline. At the start of the app, it downloads the configuration from here /AbpUserConfiguration/GetAll and store it in the cache. This configuration is where all of the server settings are stored, including localizations. I tried to modify the code to not download this endpoint if the offline but everytime the app restart, the cache is cleared out.
Instead of storing this information in the cache, which appears to clear every time the app restarts, is there a way to modify this code to store and retrieve using sqlite so that the app doesn't need to retrieve this configuration information every time the app launches?
My use case is that someone will download information from the server onto the mobile app, and then go out into the field to perform their job, where they are offline. They need to be able to use the mobile app while on the job and offline. Then they will return to their office where they have an internet connection and sync their data back to the server when they are online again.
Thanks, -Brian
to note, I generated a new solution using v6.2.0 and both iOS and Android built and ran in emulators for me without any code changes. So whatever issue I am encountering here is resolved in a later release. I will plan to upgrade.
thanks @alper . I generated a new solution using v6.2.0 and both iOS and Android built and ran in emulators for me without any code changes. So I will plan to upgrade my application to v6.2.0 before continuing on mobile development.