ServiceStack - атрибут [Authenticate] вызывается перед фильтрами запросов. Не работает с OrmLiteMultitenancyAuthRepository / OrmLiteCacheClient - PullRequest
1 голос
/ 09 ноября 2019

У меня есть приложение для нескольких арендаторов со схемой для каждого арендатора. База данных postgres

База данных содержит следующие схемы:

tenant_cf5f3518bd524c3797b884457b374e50
tenant_46255f07079046139f8c0f94290885cd
tenant_620ef0de80f74f95992742e8db4b153f
tenant_6d93b51d61ed4e33a5fb324845a83603
tenant_8e0fd300a3124de29a3070b89f3662ed
...

Я установил TenantId на IRequest.Items в async фильтр запросов.

TenantIdFilter.cs
-----------------
var aHasTenant = dto as IHaveTenant;
if (aHasTenant == null) return;
if (hasTenant.TenantId == Guid.Empty)
{
    var error = DtoUtils.CreateErrorResponse(req, HttpError.BadRequest("Missing tenant id"));
    await res.WriteToResponse(req, error);
}
if (!req.GetSession().IsAuthenticated){
    var error = DtoUtils.CreateErrorResponse(req, HttpError.Unauthorized("Unauthorized"));
    await res.WriteToResponse(req, error);
    req.Items.Remove("TenantId")
}
req.Items.Add("TenantId", hasTenant.TenantId);

...

appHost.GlobalAsyncRequestFilters.Add(new TenantIdFilter().Invoke);

Я настроил AppHost.GetDbConnection() для получения подключения к клиенту. Это все хорошо работает. Я настроил строку подключения postgres для включения.

var connectionString = AppSettings.GetString('ConnectionString');
if (req.Items.ContainsKey("TenantId")){
  var tenantId = (Guid)req.Items["TenantId"];
  connectionString = connectionString + ';Search Path=tenant_{tenantId}'
}
return new OrmLiteDbConnectionFactory(connectionString).Open()
...

Проблема возникает, когда я использую атрибуты [Authenticate] (подозреваю, что то же самое может произойти с атрибутами ролей / разрешений).

[Authenticate]
public class RestrictedDto : IReturnVoid {
}

Похоже, что фильтр [Authenticate] вызывается раньше любого GlobalRequestFilter. Это означает, что TenantId не будет установлен на IRequest.Items. И, таким образом, GetDbConnection(IRequest request) извлечет недействительный IDbConnection (не будет принимать TenantId, поскольку он еще не существует).

Когда фильтр Authenticate пытается извлечь информацию о сеансе из ICacheClient, ICacheClient (OrmLiteCacheClient) извлекает DbConnection из AppHost.GetDbConnection(). Затем выдает ошибку, что таблица cache_entry не найдена.

Я хочу, чтобы TenantIdFilter.cs был запущен перед любым атрибутом [Authenticate]. Как я могу это сделать?

Возможно, есть способ, который я мог бы переопределить в AppHost?

1 Ответ

2 голосов
/ 09 ноября 2019

Атрибут аутентификации - это Атрибут фильтра запросов , который по конструкции имеет наименьший приоритет из всех встроенных атрибутов , чтобы предотвратить выполнение неавторизованными запросами логики приложения.

Вы можете разрешить клиента в своем пользовательском GetDbConnection(), где запрос DTO будет доступен из свойства IRequest.Dto, например:

public override IDbConnection GetDbConnection(IRequest req = null)
{
    var tenantId = req?.Dto is IHaveTenant hasTenant ? hasTenant.TenantId : (Guid?) null;
}
...