Code Monkey home page Code Monkey logo

tenants's Introduction

Zonorai.Tenants ๐Ÿš€

Powered By N|Solid

This repository integrates with Finbuckle.MultiTenant using a Claim-Based Strategy and provides common use-cases that go along with multi-tenancy.

dotnet add package Zonorai.Tenants --version 1.0.7

'Don't use any package lower than 1.0.7'

Implementation Details

Functions are exposed as Mediatr Command/Query Endpoints with Notifications for each command, making it easy to extend this library.

Persistence is driven by EF CORE.

There are three providers available by default: Postgre, SqlLite, SqlServer each with migrations ready.

Clean-Architecture was used to build out this project and each respective layer has a separate nuget package.

The Application Interface Layer is for sharing requests and validators with a C# client (Blazor/Xamarin etc).

User Features

  • Registration
  • Login (JWT)
  • Update/Reset Passwords
  • Update User Details
  • Email Confirmation
  • Add to Tenant
  • Remove from Tenant
  • Assign/Unassign Claims (Is MultiTenant)

Claim Features

  • Add/Remove (Is MultiTenant)

General Features

  • Setup for JWT Authentication/Authorization provided
  • UserMultiTenantDbContext extends MultiTenantDbContext and makes it possible to create a context with relationships to users/claims.
  • Behaviours included for handling exceptions
  • Behaviours for Validation
  • Validation/Exception Behaviours specific for requests that return a Result object.

Setup

Example appsettings.json Sections

"TenantApplicationConfiguration": 
{
    "RequireConfirmedEmailForLogin": false
},
"TenantInfrastructureConfiguration": 
{
    "JWTSecret": "B83FD32C00E12636813581E8FE8F889398B37CEA3B2CB7F32C05787EAF831335",
    "ValidIssuer": "www.sometissuer.com",
    "ValidAudience": "www.something.com",
    "DbConnection": "SomeSqlConnection",
    "JwtExpirationInHours": 48,
"Provider":0 //0 = SqlLite,1 = SqlServer, 2= Postgre
},

Add Services Using AppSettings.json

services.AddZonoraiMultiTenancy(Configuration);

Manual Configuration Overloads also available

services.AddZonoraiMultiTenancy
(Configuration,
    tenantApplicationConfiguration: 
    (x) =>
    {
        x.RequireConfirmedEmailForLogin = true;
    }
);

services.AddZonoraiMultiTenancy(Configuration,
    tenantInfrastructureConfiguration: (x) =>
   {
        x.JWTSecret = "SomeBase64String";
        x.DbConnection = "SomeSqlConnection";
        x.JwtExpirationInHours = 48;
        x.ValidAudience = "www.somewhere.com";
        x.ValidIssuer = "www.someissuer.com";
    });

    services.AddZonoraiMultiTenancy(
    (appConfiguration) =>  
    {  
	     appConfiguration.RequireConfirmedEmailForLogin = true;  
    },  
     (x) =>  
    {  
	     x.JWTSecret = "SomeBase64String";  
	     x.DbConnection = "SomeSqlConnection";  
	     x.JwtExpirationInHours = 48;  
	     x.ValidAudience = "www.somewhere.com";  
	     x.ValidIssuer = "www.someissues.com";  
    }
 );

Required Middlewares

app.UseMultiTenant();

app.UseAuthentication();

app.UseAuthorization();

Examples

Registering a new tenant from a controller endpoint

[ApiController]
public class UserController : ControllerBase
{ 
    private readonly ISender _sender;
        
    public UserController(ISender sender)
    {
        _sender = sender
    }
        
    [HttpPost]
    [Route("[action]")]
    public async Task<RegisterResult> Register([FromBody] RegisterCommand command)
    {
      var result = await _sender.Send(command);
      return result;
    }
}

Reacting to registration with a confirmation email

public class RegistrationEventHandler : INotificationHandler<TenantRegisteredEvent>  
{  
      
  public Task Handle(TenantRegisteredEvent notification, CancellationToken cancellationToken)  
  {  
     ...Send an email here then direct it to an endpoint that invokes the Confirm
      Email Command
   }
}

Creating your own DbContext based on the entities provided by this library

public class UserBookmarks
{
	public string Id { get; set; }
	public string UserId { get; set; }
	public string Url { get; set; }
	public string TenantId { get; set; }
}

public class CustomDbContext : UserMultiTenantDbContext  
{  

  public CustomDbContext
  (
  ITenantInfo tenantInfo, 
  IEventStore eventStore, IMediator mediator
  ) : base(tenantInfo, eventStore, mediator)  
  { 
  
  }  
 
  public CustomDbContext
  (
      ITenantInfo tenantInfo, 
      DbContextOptions options, IEventStore eventStore,
      IMediator mediator) : base(tenantInfo, options, eventStore, mediator
  )  
  { 
  
  }
  
  protected override void OnModelCreating(ModelBuilder builder)  
  { 
      builder.Entity<UserBookmarks>().HasOne<User>().WithMany().HasForeignKey(x => x.UserId);  
      builder.Entity<UserBookmarks>().HasKey(x => x.Id);  
      builder.Entity<UserBookmarks>().IsMultiTenant();
      base.OnModelCreating(builder);  
  }  
}

If you only want to use the existing TenantDbContext then you can call migrate on the context, existing migrations are provided

var app = builder.Build();  
app.UseMultiTenant();  
app.UseAuthentication();  
app.UseAuthorization();  
var scope = app.Services.CreateScope();  
var context = scope.ServiceProvider.GetRequiredService<TenantDbContext>();  
await context.Database.MigrateAsync();

All commands have a notification that gets published once the changes are persisted This allows you to react to every action that occurs in this library effectively allowing all the extension you need.

The publishing occurs in the DbContext SaveChangesAsync method and if you inherit from UserMultiTenantDbContext you can also publish all your events post persistence.

To achieve this make use of the IEventStore interface and add your INotification object prior to calling SaveChangesAsync

Example

  public class SomeHandler : IRequestHandler<SomeRequest, Result>  
  {   
      private readonly IEventStore _eventStore; 
      private readonly TheDbContext _context;
      
      public SomeHandler(IEventStore eventStore, TheDbContext context)  
      {   
          _eventStore = eventStore;
          _context = context;  
      }  
      
      public async Task<Result> Handle(SomeRequest request, CancellationToken cancellationToken)  
      {  
     
          await _eventStore.AddEvent(new SomeEvent());  
          await _context.SaveChangesAsync(cancellationToken);  
          return Result.Ok();  
      }
  }

Commands/Notifications Table exposed by the library

Command Notification
CreateClaimCommand ClaimCreatedEvent
DeleteClaimCommand ClaimDeletedEvent
AddClaimToUserCommand ClaimAddedToUserEvent
RemoveClaimFromUserCommand ClaimRemovedFromUserEvent
AddUserCommand UserAddedEvent
ConfirmUserEmailCommand UserEmailConfirmedEvent
DeleteUserCommand UserDeletedEvent
LoginCommand UserLoggedInEvent
RegisterCommand TenantRegisteredEvent
ResetPasswordCommand PasswordResetEvent
TryLoginCommand N/A
UpdatePasswordCommand PasswordUpdatedEvent
UpdateUserDetailsCommand UserDetailsUpdatedEvent

List of queries exposed by the library

  • GetUserByEmailQuery
  • GetUserByIdQuery
  • ListUsersQuery
  • ListUserClaimsQuery
  • ListClaimsQuery

Extras

Some controllers are also availble in the net6webapi project

License

MIT

Hope this saves you some time and effort!


With <3 Zonorai

tenants's People

Contributors

zonorai avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.