Hello Servicestack,
I’m experiencing a strange behavior when I decorate a Request DTO with RequiredPermission.
Here’s the scenario :
We’ve overwritten the AuthUserSessions with the following code:
public class IFMSAuthEvents : IAuthEvents
{
public void OnAuthenticated(IRequest httpReq, IAuthSession session, IServiceBase authService, IAuthTokens tokens, Dictionary<string, string> authInfo)
{
var container = ServiceStackHost.Instance.Container;
IEnumerable<UserPermissions> permissions;
IEnumerable<UserRoles> roles;
using (var db = container.ResolveNamed<IDbConnectionFactory>(Core.Resources.IFMSDbFactory).Open())
{
permissions = db.Select<UserPermissions>(x => x.UserName == session.UserName || x.UserName == session.UserAuthName);
roles = db.Select<UserRoles>(x => x.UserName == session.UserName || x.UserName == session.UserAuthName);
}
if (session.Permissions == null)
session.Permissions = new List<string>();
foreach (var p in permissions)
{
if (!session.Permissions.ContainsIgnoreCase(p.PermissionName))
session.Permissions.Add(p.PermissionName);
}
if (session.Roles == null)
session.Roles = new List<string>();
foreach (var r in roles)
{
if (!session.Roles.ContainsIgnoreCase(r.RoleName))
session.Roles.Add(r.RoleName);
}
httpReq.SaveSession(session);
var userSession = session as IFMSAuthUserSession;
if (userSession != null)
{
var modules = userSession.GetAuthorizedModules();
modules.ForEach(m => Log.Information("Found module {Name}", m.Name));
var menu = userSession?.Menu;
if (menu == null)
{
if (AppHost.AppSettings.Get(Core.Resources.PersistSessionKey, true))
{
httpReq.SaveSession(session);
}
menu = session.BuildMenu(modules);
if (userSession != null)
{
userSession.Menu = menu;
}
}
}
}
public void OnCreated(IRequest httpReq, IAuthSession session)
{
// Method intentionally left empty.
}
public void OnLogout(IRequest httpReq, IAuthSession session, IServiceBase authService)
{
Log.Information("User {UserAuthName} logout", session.UserAuthName);
ServiceStackHost.Instance.Container.Resolve<ICacheClient>().Remove(SessionFeature.GetSessionKey(httpReq.GetSessionId()));
}
public void OnRegistered(IRequest httpReq, IAuthSession session, IServiceBase registrationService)
{
// Method intentionally left empty.
}
}
The DTO that’s facing problem is the following
[Route(Route, Verbs = "POST")]
[Authenticate]
[RequiredPermission(Route)]
public class FileDownload : INullableFileType, ITenant<int?>, IReturn<FileStreamResult>
{
const string Route = "/file/download";
public int? FileId { get; set; }
public int? FileTypeId { get; set; }
public int? TenantId { get; set; }
}
I’m using ServiceStack’s API Key as AuthenticationProvider
After authentication , in the OnAuthenticatedMethod I’m filling property Permissions with values fetched from DB.
My implementation of AuthUserSession is
public class IFMSAuthUserSession : AuthUserSession
#pragma warning restore S101 // Types should be named in camel case
{
public IFMSAuthUserSession()
{
CustomInfo = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
}
IList<IModule> AuthorizedModules { get; set; }
IModule CurrentModule => HostContext.Resolve<IModuleResolver>().CurrentModule;
public IMenu Menu { get; set; }
public IList<IModule> GetAuthorizedModules() =>
AuthorizedModules
?? (AuthorizedModules = Core.Resources.ModuleResolver.GetAuthorizedModules(this));
public string UserImpersonated { get; set; }
//Modificato IDictionary in Dictionary perché servicestack prevede degli extension method solo per Dictionary
public Dictionary<string, object> CustomInfo { get; set; }
/// <summary>
/// Restituisce se l'utente ha tutti i ruoli specificati.
/// </summary>
public bool HasAllRoles(IEnumerable<string> roles, IAuthRepository authRepo)
{
return roles.All(y => HasRole(y, authRepo));
}
/// <summary>
/// Restituisce se l'utente ha almeno uno dei ruoli specificati.
/// </summary>
public bool HasAnyRoles(IEnumerable<string> roles, IAuthRepository authRepo)
{
return roles.Any(y => HasRole(y, authRepo));
}
/// <summary>
/// Restituisce se l'utente ha tutti i permessi specificati.
/// </summary>
public bool HasAllPermission(IEnumerable<string> permissions, IAuthRepository authRepo)
{
return permissions.All(y => HasPermission(y, authRepo));
}
/// <summary>
/// Restituisce se l'utente ha almeno uno dei permessi specificati.
/// </summary>
public bool HasAnyPermission(IEnumerable<string> permissions, IAuthRepository authRepo)
{
return permissions.Any(y => HasPermission(y, authRepo));
}
/// <summary>
/// Se il module implementa l'interfaccia <see cref="IHasPermission"/> allora si utilizza la logica implementata, altrimenti si utilizza la logica di default.
/// </summary>
/// <param name="permission"></param>
/// <returns></returns>
public override bool HasPermission(string permission, IAuthRepository authRepo)
{
var module = CurrentModule as IHasPermission;
return module?.HasPermission(permission) ?? Permissions != null && Permissions.Contains(permission);
}
/// <summary>
/// Se il module implementa l'interfaccia <see cref="IHasRole"/> allora si utilizza la logica implementata, altrimenti si utilizza la logica di default.
/// </summary>
/// <param name="role"></param>
/// <returns></returns>
public override bool HasRole(string role, IAuthRepository authRepo)
{
var module = CurrentModule as IHasRole;
return module?.HasRole(role) ?? Roles != null && Roles.Contains(role);
}
}
In the HasPermission method sometimes I’ve got property Permissions correctly filled, in the same session Permissions become empty. Doing some debugging I’ve seen that the CallStack is the following
It seems that the method UpdateFromUserAuthRepo overwrites the property Permissions
Any workaround?