You might also find this useful for scheduling inside of Hangfire. https://www.twilio.com/docs/sms/tutorials/appointment-reminders-csharp-mvc
@maliming, I will investigate further by placing some log statements in the GetCurrentLoginInformations method on the server and also place some console.log statements in angular. I'm not sure what this means exactly (in terms of what I physically have to do):
In order to rule out the problem of azure. You can deploy multiple domain name environments locally, modify the domain name in your computer's Host file, and then bind the domain name in IIS, which helps to debug the problem.
So I may come back to you to explain this if I reach another dead-end. Thanks for your assistance and guidance so far.
@maliming, thanks for the reply but I still have no luck. My appconfig.json file looks as follows:
{
"remoteServiceBaseUrl": "http://{TENANCY_NAME}.projectname.api",
"appBaseUrl": "http://{TENANCY_NAME}.projectname.ui"
}
I have changed my Azure settings accordingly:
I am expecting the page to pick up the tenant from the sub-domain in the browser bar. I have applied some debug statements in the login page. Here is an example after I have impersonated a user in the tenant and logged out:
If you traverse to [subdomain.websitehere] you will see that the debug value for tenant session object is null.
Any ideas anyone, I'm kind of getting a bit desperate now and definitely getting balder as I tear more of my hair out....
.net core, angular, 5.4.1 I have a multi-tenanted solution set up at [websitehere]. The system is hosted on azure [websitehere.azurewebsites.net] and my domains are held at GoDaddy. I set up a new tenant and send the link to my customer by email.
When the customer clicks on the link s/he is directed to the website and although we can see the sub-domain in the browser bar:"subdomain.website.api" the tenant is not picked up by the system and the customer is unable to login. However, I can sit with the customer and login using the host admin details with the sub-domain showing in the browser bar which logs me into the host account. I can then impersonate a user in the tenant account, return to my host account and logout. Now when the user traverses to "subdomain.website.api" the tenant is found and the customer is able to login. I have spent a few calls and hours with GoDaddy and they confirm my DNS settings are correct:
I am told my Azure settings are also fine:
My appconfig.json is setup as standard:
{
"remoteServiceBaseUrl": "https://projectname.api",
"appBaseUrl": "https://{TENANCY_NAME}.projectname.ui",
"localeMappings": [
{
"from": "pt-BR",
"to": "pt"
},
{
"from": "zh-CN",
"to": "zh"
}
]
}
Everything points to a code problem and i have placed comments in account.component.htm,l to verify the tenant is not found on first visit to the sub-domain. Why is the tenant not being picked up on first visit to the page, subdomain.website.api? Any help much appreciated because this is becoming a bit of a show-stopper for me.
Hi Vlad, I think the key here is to override EntityHistoryStore. Paste a copy (I seem to tremember it's in Boilerplate) and inject in ProjectNameCoreModule:
Configuration.ReplaceService<IEntityHistoryStore, YourEntityHistoryStore>(DependencyLifeStyle.Transient);
Then modify the SaveAsync method accordingly. Personally I choose to save entity history in Mongodb because of the size of data (I also use Mongodb for audit log entries) so my personal override uses a Mongodb interface. Let me know if you want the code and I will post accordingly but, judging by your product, your guys seem to be proficient enough to take this forward.
Hi Jasen, I took a different approach and successfully hooked into Nuagecare.Migrations.Seed.SeedHelper:
public static void SeedHostDb(NuagecareDbContext context)
{
context.SuppressAutoSetTenantId = true;
//Host seed
new InitialHostDbBuilder(context).Create();
//Default tenant seed (in host database).
new DefaultTenantBuilder(context).Create();
new DefaultTenantDataBuilder(context, 1).Create();
var tenants = context.Tenants.ToList();
foreach (var tenant in tenants)
{
new TenantRoleAndUserBuilder(context, tenant.Id).Create();
}
}
And then, within TenantRoleAndUserBuilder(context, tenant.Id).Create():
public void Create()
{
CreateRolesAndUsers();
}
And, in CreateRolesAndUsers() here is a sample role with base permissions allocated:
var carerRole = _context.Roles.IgnoreQueryFilters().FirstOrDefault(r => r.TenantId == _tenantId && r.Name == StaticRoleNames.Tenants.Carer);
if (carerRole == null)
{
carerRole = _context.Roles.Add(new Role(_tenantId, StaticRoleNames.Tenants.Carer, StaticRoleNames.Tenants.Carer) { IsStatic = true, IsDefault = true }).Entity;
_context.SaveChanges();
//Grant selected permissions to carer role
var permissions = PermissionFinder
.GetAllPermissions(new AppAuthorizationProvider(false))
.Where(p => p.MultiTenancySides.HasFlag(MultiTenancySides.Tenant))
.ToList();
foreach (var permission in permissions)
{
if (
permission.Name == "Pages.Tenant.Dashboard" ||
permission.Name == "Pages.NcEntities" ||
permission.Name == "Pages.NcEntity.Display" ||
permission.Name == "Pages.NcEntity.Display.Dashboard" ||
permission.Name == "Pages.NcEntity.Display.Profile" ||
permission.Name == "Pages.NcEntity.Display.Profile.Metrics" ||
permission.Name == "Pages.NcEntity.NcCarePlans" ||
permission.Name == "Pages.NcEntity.NcCarePlan.Read" ||
permission.Name == "Pages.NcEntity.NcWarnings"
)
{
_context.Permissions.Add(
new RolePermissionSetting
{
TenantId = _tenantId,
Name = permission.Name,
IsGranted = true,
RoleId = carerRole.Id
});
}
}
_context.SaveChanges();
}
That will create your roles with default permissions across all tenants.
My attempts to do exactly this are documented here: https://support.aspnetzero.com/QA/Questions/5873 My thoughts were to leverage the seed helper class to call each tenant:
public static void SeedHostDb(NuagecareDbContext context)
{
context.SuppressAutoSetTenantId = true;
//Host seed
new InitialHostDbBuilder(context).Create();
//Default tenant seed (in host database).
new DefaultTenantBuilder(context).Create();
//how do I get all tenants here?
//foreach (var tenant in tenants)....
new TenantRoleAndUserBuilder(context, tenant.id).Create();
new DefaultTenantDataBuilder(context, tenant.id).Create();
}
and then, in my_ TenantRoleAndUserBuilder.Create()_ method:
private void CreateRolesAndUsers()
{
//Carer role
_context.Roles.Add(new Role(_tenantId, StaticRoleNames.Tenants.Carer, StaticRoleNames.Tenants.Carer) { IsStatic = true, IsDefault = true });
var carerRole = _context.Roles.IgnoreQueryFilters().FirstOrDefault(r => r.TenantId == _tenantId && r.Name == StaticRoleNames.Tenants.Carer);
if (carerRole == null)
{
//Grant permissions to carer role
var permissions = PermissionFinder
.GetAllPermissions(new AppAuthorizationProvider(false))
.Where(p => p.MultiTenancySides.HasFlag(MultiTenancySides.Tenant))
.ToList();
foreach (var permission in permissions)
{
if (
permission.Name == "Pages.Tenant.Dashboard" ||
permission.Name == "Pages.NcEntities" ||
permission.Name == "Pages.NcEntity.Display" ||
permission.Name == "Pages.NcEntity.Display.Dashboard" ||
permission.Name == "Pages.NcEntity.Display.Profile" ||
permission.Name == "Pages.NcEntity.Display.Profile.Metrics" ||
permission.Name == "Pages.NcEntity.NcCarePlans" ||
permission.Name == "Pages.NcEntity.NcCarePlan.Read" ||
permission.Name == "Pages.NcEntity.NcWarnings"
)
{
_context.Permissions.Add(
new RolePermissionSetting
{
TenantId = _tenantId,
Name = permission.Name,
IsGranted = true,
RoleId = carerRole.Id
});
}
}
_context.SaveChanges();
}
}
However, I can't work out how to get the list of tenants in the seed helper class. Is there a solution to the problem of creating roles for tenants with pre-defined permissions?
Because within the method: TenantRoleAndUserBuilder(context, tenant.id).Create(); I am setting permissions and need the tenant number.:
private void CreateRolesAndUsers()
{
//Staff role
_context.Roles.Add(new Role(_tenantId, StaticRoleNames.Tenants.Carer, StaticRoleNames.Tenants.Carer) { IsStatic = true, IsDefault = true });
var carerRole = _context.Roles.IgnoreQueryFilters().FirstOrDefault(r => r.TenantId == _tenantId && r.Name == StaticRoleNames.Tenants.Carer);
if (carerRole == null)
{
//Grant all permissions to admin role
var permissions = PermissionFinder
.GetAllPermissions(new AppAuthorizationProvider(false))
.Where(p => p.MultiTenancySides.HasFlag(MultiTenancySides.Tenant))
.ToList();
foreach (var permission in permissions)
{
if (
permission.Name == "Pages.Tenant.Dashboard" ||
permission.Name == "Pages.NcEntities" ||
permission.Name == "Pages.NcEntity.Display" ||
permission.Name == "Pages.NcEntity.Display.Dashboard" ||
permission.Name == "Pages.NcEntity.Display.Profile" ||
permission.Name == "Pages.NcEntity.Display.Profile.Metrics" ||
permission.Name == "Pages.NcEntity.NcCarePlans" ||
permission.Name == "Pages.NcEntity.NcCarePlan.Read" ||
permission.Name == "Pages.NcEntity.NcWarnings"
)
{
_context.Permissions.Add(
new RolePermissionSetting
{
TenantId = _tenantId,
Name = permission.Name,
IsGranted = true,
RoleId = carerRole.Id
});
}
}
_context.SaveChanges();
}
}
And therefore i would like to seed each tenant indivdiually.
I am trying to introduce a new set of roles for each tenant through the Nuagecare.Migrations.Seed.SeedHelper method. How do I get all tenants in the static SeedHelper class?
public static void SeedHostDb(NuagecareDbContext context)
{
context.SuppressAutoSetTenantId = true;
//Host seed
new InitialHostDbBuilder(context).Create();
//Default tenant seed (in host database).
new DefaultTenantBuilder(context).Create();
//how do I get all tenants here?
//foreach (var tenant in tenants)....
new TenantRoleAndUserBuilder(context, tenant.id).Create();
new DefaultTenantDataBuilder(context, tenant.id).Create();
}