Agrege tabla de tenancy usando Fluent Migrator e incluye la columna “TenantId” en las tablas que ocupen ser filtradas por tenancy

using FluentMigrator;

namespace MultiTenancy.Migrations.DefaultDB

{

[Migration(20170203000002)]

public class DefaultDb_20170203_000002_MultiTenancy

: AutoReversingMigration

{

public override void Up()

{

Create.Table("Tenants")

.WithColumn("TenantId").AsInt32()

.Identity().PrimaryKey().NotNullable()

.WithColumn("TenantName").AsString(100)

.NotNullable();

Insert.IntoTable("Tenants")

.Row(new

{

TenantName = "Admin Tenant" //Your first tenant, the Admin

});

Alter.Table("Users")

.AddColumn("TenantId").AsInt32()

.NotNullable().WithDefaultValue(1);

Alter.Table("Roles")

.AddColumn("TenantId").AsInt32()

.NotNullable().WithDefaultValue(1);

Alter.Table("Languages")

.AddColumn("TenantId").AsInt32()

.NotNullable().WithDefaultValue(1);

//Your Tables, Repeat for each table needing tenancy

Alter.Table("Employees")

.AddColumn("TenantId").AsInt32()

.NotNullable().WithDefaultValue(1);

}

}

}

Nota: Las tablas UserPermissions, UserRoles, RolePermissions etc, ya tienen el comportamiento de tenancy usando UserID o RoleId asi que no ocupan el TenantId.

Usa Sergen para generar los archivos neccesarios igual a cualqier otra tabla.

Agrega un LookupScript a la tabla TenantRow en TenantRow.cs, Junto con la clave de permisso solo para el administrardor

En AdministrationPermissionKeys.cs

namespace MultiTenancy.Administration

{

public class PermissionKeys

{

public const string Security = "Administration:Security";

public const string Translation = "Administration:Translation";

public const string Tenants = "Administration:Tenants";

}

}

En Tenantrow.cs

namespace MultiTenancy.Administration.Entities

{

//...

[ConnectionKey("Default"), DisplayName("Tenants"),

InstanceName("Tenant"), TwoLevelCached]

[ReadPermission(PermissionKeys.Tenants)]

[ModifyPermission(PermissionKeys.Tenants)]

[LookupScript("Administration.Tenant")]

public sealed class TenantRow : Row, IIdRow, INameRow

{

[DisplayName("Tenant Id"), Identity]

public Int32? TenantId

{

get { return Fields.TenantId[this]; }

set { Fields.TenantId[this] = value; }

}

//...

Agrega TenantId a UserRow.cs

public sealed class UserRow : LoggingRow, IIdRow, INameRow

{

//...

[DisplayName("Last Directory Update"), Insertable(false), Updatable(false)]

public DateTime? LastDirectoryUpdate

{

get { return Fields.LastDirectoryUpdate[this]; }

set { Fields.LastDirectoryUpdate[this] = value; }

}

[DisplayName("Tenant"), ForeignKey("Tenants", "TenantId"), LeftJoin("tnt")]

[LookupEditor(typeof(TenantRow))]

public Int32? TenantId

{

get { return Fields.TenantId[this]; }

set { Fields.TenantId[this] = value; }

}

[DisplayName("Tenant"), Expression("tnt.TenantName")]

public String TenantName

{

get { return Fields.TenantName[this]; }

set { Fields.TenantName[this] = value; }

}

//...

public class RowFields : LoggingRowFields

{

//...

public readonly DateTimeField LastDirectoryUpdate;

public readonly Int32Field TenantId;

public readonly StringField TenantName;

//...

Tambien agreagalas a el Formulario

namespace MultiTenancy.Administration.Forms

{

using Serenity;

using Serenity.ComponentModel;

using System;

using System.ComponentModel;

[FormScript("Administration.User")]

[BasedOnRow(typeof(Entities.UserRow))]

public class UserForm

{

public String Username { get; set; }

public String DisplayName { get; set; }

[EmailEditor]

public String Email { get; set; }

[PasswordEditor]

public String Password { get; set; }

[PasswordEditor, OneWay]

public String PasswordConfirm { get; set; }

[OneWay]

public string Source { get; set; }

public Int32? TenantId { get; set; }

}

}

En la carpeta Administration>User>Authenctication abre UserDefintion.cs y agrega

{

using Serenity;

using System;

[Serializable]

public class UserDefinition : IUserDefinition

{

public string Id { get { return UserId.ToInvariant(); } }

public string DisplayName { get; set; }

public string Email { get; set; }

public short IsActive { get; set; }

public int UserId { get; set; }

public string Username { get; set; }

public string PasswordHash { get; set; }

public string PasswordSalt { get; set; }

public string Source { get; set; }

public DateTime? UpdateDate { get; set; }

public DateTime? LastDirectoryUpdate { get; set; }

public int TenantId { get; set; }

}

}

En UserRetrieveService.cs bajo ‘GetFirst’ haz el siguiente cambio

private UserDefinition GetFirst(IDbConnection connection, BaseCriteria criteria)

{

var user = connection.TrySingle<Entities.UserRow>(criteria);

if (user != null)

return new UserDefinition

{

UserId = user.UserId.Value,

Username = user.Username,

Email = user.Email,

DisplayName = user.DisplayName,

IsActive = user.IsActive.Value,

Source = user.Source,

PasswordHash = user.PasswordHash,

PasswordSalt = user.PasswordSalt,

UpdateDate = user.UpdateDate,

LastDirectoryUpdate = user.LastDirectoryUpdate,

TenantId = user.TenantId.Value

};

return null;

}

En UserRepository.cs busca la classe MyListHandler y modificala

private class MyListHandler : ListRequestHandler<MyRow>

{

protected override void ApplyFilters(SqlQuery query)

{

base.ApplyFilters(query);

var user = (UserDefinition)Authorization.UserDefinition;

if (!Authorization.HasPermission(PermissionKeys.Tenants))

query.Where(fld.TenantId == user.TenantId);

}

}

En el mismo archive busca MySaveHandler y modifica el ‘GetEditableFields’

if (!Authorization.HasPermission(Administration.PermissionKeys.Security))

{

editable.Remove(fld.Source);

editable.Remove(fld.IsActive);

}

if (!Authorization.HasPermission(Administration.PermissionKeys.Tenants))

{

editable.Remove(fld.TenantId);

}

Igualmente en el mismo archive busca ‘SetInternalFields’ y modifica:

if (IsCreate)

{

Row.Source = "site";

Row.IsActive = Row.IsActive ?? 1;

if (!Authorization.HasPermission(Administration.PermissionKeys.Tenants) ||

Row.TenantId == null)

{

Row.TenantId = ((UserDefinition)Authorization.UserDefinition)

.TenantId;

}

}

Igualmente en el mismo archivo busca ‘MyRetrieveHandler’ y modifica:

private class MyRetrieveHandler : RetrieveRequestHandler<MyRow>

{

protected override void PrepareQuery(SqlQuery query)

{

base.PrepareQuery(query);

var user = (UserDefinition)Authorization.UserDefinition;

if (!Authorization.HasPermission(PermissionKeys.Tenants))

query.Where(fld.TenantId == user.TenantId);

}

}

Igualmente en el mismo archivo busca ‘ValidateRequest’ bajo MySaveHandler y modifica:

protected override void ValidateRequest()

{

base.ValidateRequest();

if (IsUpdate)

{

var user = (UserDefinition)Authorization.UserDefinition;

if (Old.TenantId != user.TenantId)

Authorization.ValidatePermission(PermissionKeys.Tenants);

//…

Ahora busca MyDeleteHandler y MyUndeleteHandler y modifica

private class MyDeleteHandler : DeleteRequestHandler<MyRow>

{

protected override void ValidateRequest()

{

base.ValidateRequest();

var user = (UserDefinition)Authorization.UserDefinition;

if (Row.TenantId != user.TenantId)

Authorization.ValidatePermission(PermissionKeys.Tenants);

}

}

private class MyUndeleteHandler : UndeleteRequestHandler<MyRow>

{

protected override void ValidateRequest()

{

base.ValidateRequest();

var user = (UserDefinition)Authorization.UserDefinition;

if (Row.TenantId != user.TenantId)

Authorization.ValidatePermission(PermissionKeys.Tenants);

}

}

En UserDialog.ts agrega:

protected getPropertyItems() {

let items = super.getPropertyItems();

if (!Authorization.hasPermission("Administration:Tenants"))

items = items.filter(x => x.name != UserRow.Fields.TenantId);

return items;

}

Asegurate que Authorization.ts tiene:

export function hasPermission(permissionKey: string) {

let ud = userDefinition;

return ud.Username === 'admin' || !!ud.Permissions[permissionKey];

En UserPermissionRepository.cs busca por “ListPermissionKeys” y modifica

public ListResponse<string> ListPermissionKeys()

{

return LocalCache.Get("Administration:PermissionKeys", TimeSpan.Zero, () =>

{

//...

result.Remove(Administration.PermissionKeys.Tenants);

result.Remove("*");

result.Remove("?");

//...

Modifica UserPermissionRepository.cs bajo ‘Update’

public class UserPermissionRepository

{

public SaveResponse Update(IUnitOfWork uow,

UserPermissionUpdateRequest request)

{

//...

var newList = new Dictionary<string, bool>(

StringComparer.OrdinalIgnoreCase);

foreach (var p in request.Permissions)

newList[p.PermissionKey] = p.Grant ?? false;

var allowedKeys = ListPermissionKeys()

.Entities.ToDictionary(x => x);

if (newList.Keys.Any(x => !allowedKeys.ContainsKey(x)))

throw new AccessViolationException();

//...

Modifica RolePermissionRepository.cs bajo ‘Update’

public class RolePermissionRepository

{

public SaveResponse Update(IUnitOfWork uow,

RolePermissionUpdateRequest request)

{

//...

var newList = new HashSet<string>(

request.Permissions.ToList(),

StringComparer.OrdinalIgnoreCase);

var allowedKeys = new UserPermissionRepository()

.ListPermissionKeys()

.Entities.ToDictionary(x => x);

if (newList.Any(x => !allowedKeys.ContainsKey(x)))

throw new AccessViolationException();

//...

Agreaga TenantId a RoleRow.cs

namespace MultiTenancy.Administration.Entities

{

//...

public sealed class RoleRow : Row, IIdRow, INameRow

{

[Insertable(false), Updatable(false)]

public Int32? TenantId

{

get { return Fields.TenantId[this]; }

set { Fields.TenantId[this] = value; }

}

//...

public class RowFields : RowFieldsBase

{

//...

public readonly Int32Field TenantId;

//...

}

}

}

Haz un Nuevo archive llamado IMultiTenantRow.cs junto a TenantRow.cs y agrega la siguiente interface.

using Serenity.Data;

namespace MultiTenancy

{

public interface IMultiTenantRow

{

Int32Field TenantIdField { get; }

}

}

Haz un Nuevo archive llamado MultiTenantBehavior.cs junto a TenantRow.cs y agrega lo siguente

using MultiTenancy.Administration;

using Serenity;

using Serenity.Data;

using Serenity.Services;

namespace MultiTenancy

{

public class MultiTenantBehavior : IImplicitBehavior,

ISaveBehavior, IDeleteBehavior,

IListBehavior, IRetrieveBehavior

{

private Int32Field fldTenantId;

public bool ActivateFor(Row row)

{

var mt = row as IMultiTenantRow;

if (mt == null)

return false;

fldTenantId = mt.TenantIdField;

return true;

}

public void OnPrepareQuery(IRetrieveRequestHandler handler,

SqlQuery query)

{

var user = (UserDefinition)Authorization.UserDefinition;

if (!Authorization.HasPermission(PermissionKeys.Tenants))

query.Where(fldTenantId == user.TenantId);

}

public void OnPrepareQuery(IListRequestHandler handler,

SqlQuery query)

{

var user = (UserDefinition)Authorization.UserDefinition;

if (!Authorization.HasPermission(PermissionKeys.Tenants))

query.Where(fldTenantId == user.TenantId);

}

public void OnSetInternalFields(ISaveRequestHandler handler)

{

if (handler.IsCreate)

fldTenantId[handler.Row] =

((UserDefinition)Authorization

.UserDefinition).TenantId;

}

public void OnValidateRequest(IDeleteRequestHandler handler)

{

var user = (UserDefinition)Authorization.UserDefinition;

if (fldTenantId[handler.Row] != user.TenantId)

Authorization.ValidatePermission(

PermissionKeys.Tenants);

}

public void OnAfterDelete(IDeleteRequestHandler handler) { }

public void OnAfterExecuteQuery(IRetrieveRequestHandler handler) { }

public void OnAfterExecuteQuery(IListRequestHandler handler) { }

public void OnAfterSave(ISaveRequestHandler handler) { }

public void OnApplyFilters(IListRequestHandler handler, SqlQuery query) { }

public void OnAudit(IDeleteRequestHandler handler) { }

public void OnAudit(ISaveRequestHandler handler) { }

public void OnBeforeDelete(IDeleteRequestHandler handler) { }

public void OnBeforeExecuteQuery(IRetrieveRequestHandler handler) { }

public void OnBeforeExecuteQuery(IListRequestHandler handler) { }

public void OnBeforeSave(ISaveRequestHandler handler) { }

public void OnPrepareQuery(IDeleteRequestHandler handler, SqlQuery query) { }

public void OnPrepareQuery(ISaveRequestHandler handler, SqlQuery query) { }

public void OnReturn(IDeleteRequestHandler handler) { }

public void OnReturn(IRetrieveRequestHandler handler) { }

public void OnReturn(IListRequestHandler handler) { }

public void OnReturn(ISaveRequestHandler handler) { }

public void OnValidateRequest(IRetrieveRequestHandler handler) { }

public void OnValidateRequest(IListRequestHandler handler) { }

public void OnValidateRequest(ISaveRequestHandler handler) { }

}

}

En RoleRow.cs Agrega el Nuevo comportamiento:

namespace MultiTenancy.Administration.Entities

{

//...

public sealed class RoleRow : Row, IIdRow, INameRow, IMultiTenantRow

{

//...

public Int32Field TenantIdField

{

get { return Fields.TenantId; }

}

//...

}

}

En el resto de los Row.cs que le pusiste TenantId, Implementa el nuevo comportaimento junto con la columna para TenantId.

namespace MultiTenancy.Administration.Entities

{

//...

public sealed class RoleRow : Row, IIdRow, INameRow, IMultiTenantRow

{

//...

[Insertable(false), Updatable(false)]

public Int32? TenantId

{

get { return Fields.TenantId[this]; }

set { Fields.TenantId[this] = value; }

}

public Int32Field TenantIdField

{

get { return Fields.TenantId; }

}

//...

public class RowFields : RowFieldsBase

{

//...

public readonly Int32Field TenantId;

}

}

}

}

results matching ""

    No results matching ""