How to migrate a cached ServiceStack session to a new "version" - session

When we add new properties to our custom AuthUserSession based session DTO, we either need to invalidate users active sessions and force them to re-login, or migrate their sessions (either in mass, or in lazy fashion). If this is not done, expected properties will not be filled, and adds a lot more complexity to the code relying on those properties.
I dug around and looked for any events around hydration of sessions from cache, but didn't see any easy place to tie in and determine if the session should be refreshed.
Any suggestions on where to plug in such logic in the flow where it will always happen before some session object is used by a ServiceStack Service or Razor view?

For Caching providers that implement ICacheClientExtended you can access all Sessions with:
var sessionPattern = IdUtils.CreateUrn<IAuthSession>(""); //= urn:iauthsession:
var sessionKeys = Cache.GetKeysStartingWith(sessionPattern).ToList();
var allSessions = Cache.GetAll<IAuthSession>(sessionKeys);
Otherwise I've just added a custom hook to be able to filter a session (in this commit), by overriding OnSessionFilter() in your AppHost, e.g:
public override IAuthSession OnSessionFilter(IAuthSession session, string id)
{
return base.OnSessionFilter(session, id);
}
This change is available from v4.0.49 that's now available from MyGet.

Related

What is the best way to manage a user's session in React?

I have a doubt about how to manage a user's session in React, for example in MVC .NET you only just do this using the Session object (e.g. Session["test"] = "";), but obviously React can not do this.
I was reading about using the component state, I suppose that sets the state at the principal component and passes this state to other components using props. Also I saw people recommending the use the browser's localStorage or cookies, but I don't know if this if a good idea or practice.
Is there a better way to manage sessions variables in React than localStorage or cookies?
I would avoid using component state since this could be difficult to manage and prone to issues that can be difficult to troubleshoot.
You should use either cookies or localStorage for persisting a user's session data. You can also use a closure as a wrapper around your cookie or localStorage data.
Here is a simple example of a UserProfile closure that will hold the user's name.
var UserProfile = (function() {
var full_name = "";
var getName = function() {
return full_name; // Or pull this from cookie/localStorage
};
var setName = function(name) {
full_name = name;
// Also set this in cookie/localStorage
};
return {
getName: getName,
setName: setName
}
})();
export default UserProfile;
When a user logs in, you can populate this object with user name, email address etc.
import UserProfile from './UserProfile';
UserProfile.setName("Some Guy");
Then you can get this data from any component in your app when needed.
import UserProfile from './UserProfile';
UserProfile.getName();
Using a closure will keep data outside of the global namespace, and make it is easily accessible from anywhere in your app.
There is a React module called react-client-session that makes storing client side session data very easy. The git repo is here.
This is implemented in a similar way as the closure approach in my other answer, however it also supports persistence using 3 different persistence stores. The default store is memory(not persistent).
Cookie
localStorage
sessionStorage
After installing, just set the desired store type where you mount the root component ...
import { ReactSession } from 'react-client-session';
ReactSession.setStoreType("localStorage");
... and set/get key value pairs from anywhere in your app:
import { ReactSession } from 'react-client-session';
ReactSession.set("username", "Bob");
ReactSession.get("username"); // Returns "Bob"
This not the best way to manage session in react you can use web tokens to encrypt your data that you want save,you can use various number of services available a popular one is JSON web tokens(JWT) with web-tokens you can logout after some time if there no action from the client And after creating the token you can store it in your local storage for ease of access.
jwt.sign({user}, 'secretkey', { expiresIn: '30s' }, (err, token) => {
res.json({
token
});
user object in here is the user data which you want to keep in the session
localStorage.setItem('session',JSON.stringify(token));
To name a few we can use redux-react-session which is having good API for session management like, initSessionService, refreshFromLocalStorage, checkAuth and many other. It also provide some advanced functionality like Immutable JS.
Alternatively we can leverage react-web-session which provides options like callback and timeout.

Stop EPiServer clearing output cache on publish

This isn't a question I've seen around, usually it's 'EPiServer isn't clearing the output cache'. I'm trying to achieve the opposite. Each time a page is published the entire cache is dropped and as the client publishes several times a day, this is frustrating.
I'm using the [ContentOutputCache] attribute and tried to implement a httpCacheVaryByCustom rule with an accompanying scheduled task in EPiServer to invalidate the cache when we decide to i.e. bundle updates together and invalidate at a predetermined time.
I've tested this rule and it works using:
public override string GetVaryByCustomString(HttpContext context, string custom)
I was under the impression that by using this type of caching rule it would stop EPiServer dumping my cache whenever something is published / media uploaded.
It doesn't though is there a way to stop this from happening?
I've had success by using the standard [OutputCache] with the same custom string rule the only problem with this is that editors will always see a cached version of the page they are editing.
The application settings I have in my web.config for EPiServer are:
<applicationSettings globalErrorHandling="Off" operationCompatibility="DynamicProperties" uiSafeHtmlTags="b,i,u,br,em,strong,p,a,img,ol,ul,li" disableVersionDeletion="false"
httpCacheability="Public" uiEditorCssPaths="~/assets/css/styles.css, ~/assets/css/editor.css" urlRebaseKind="ToRootRelative"
pageUseBrowserLanguagePreferences="false" uiShowGlobalizationUserInterface="false" subscriptionHandler="EPiServer.Personalization.SubscriptionMail,EPiServer"
uiMaxVersions="20" pageValidateTemplate="false" utilUrl="~/util/"
uiUrl="~/EPiServer/CMS/" httpCacheExpiration="01:00:00" httpCacheVaryByCustom="invalidateSiteCache" />
A custom GetVaryByCustomString function will determine when the cache is invalidated, but any request for content that is using the ContentOutputCache is checked against a master cache key Episerver.DataFactoryCache.Version. This version number is incremented any time content is published, updated etc, and the cache is invalidated if the version number is changed.
To understand what you need to do, I recommend using a decompiler (e.g. DotPeek) and looking at the ContentOutputCacheAttribute and OutputCacheHandler classes in the Episerver dll.
You will need to:
Derive a new handler from EPiServer.Web.OutputCacheHandler
Create an alternative method to ValidateOutputCache(...) that still calls OutputCacheHandler.UseOutputCache(...) but ignores the cache version number
Derive a new attribute from ContentOutputCacheAttribute
Override the method OnResultExecuting(ResultExecutingContext filterContext) using the same logic as the current method (this is where a decompiler is useful), but that adds a callback to your new validate method instead of the current one. Unfortunately we can't inject the new handler because the validate method is passed statically.
e.g.
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
// Rest of method
filterContext.HttpContext.Response.Cache.AddValidationCallback(new HttpCacheValidateHandler(CustomOutputCacheHandler.CustomValidateOutputCache), (object) tuple);
}
Use the new attribute in place of [ContentOutputCache]

Security: Session Identifier Not Updated in tcl

I'm working on open-source application "Project-Open" and during the scanning I got the following vulnerability:
[Medium] Session Identifier Not Updated
Issue: 13800882
Severity: Medium
URL: https://<server_name>/register/
Risk(s): It is possible to steal or manipulate customer session and cookies, which might be used to impersonate a legitimate user,allowing the hacker to view or alter user records, and to perform transactions as that user
Fix: Do not accept externally created session identifiers
though the fix is mentioned but it is not sufficient for me to understand it completely.please guide me how should I remove this.Also let me know if any further details are needed to understand the question.
The project source code is in tcl
I found the following code which does the same but it's in java.
public HttpSession changeSessionIdentifier(HttpServletRequest request) throws AuthenticationException {
// get the current session
HttpSession oldSession = request.getSession();
// make a copy of the session content
Map<String,Object> temp = new ConcurrentHashMap<String,Object>();
Enumeration e = oldSession.getAttributeNames();
while (e != null && e.hasMoreElements()) {
String name = (String) e.nextElement();
Object value = oldSession.getAttribute(name);
temp.put(name, value);
}
// kill the old session and create a new one
oldSession.invalidate();
HttpSession newSession = request.getSession();
User user = ESAPI.authenticator().getCurrentUser();
user.addSession( newSession );
user.removeSession( oldSession );
// copy back the session content
for (Map.Entry<String, Object> stringObjectEntry : temp.entrySet()){
newSession.setAttribute(stringObjectEntry.getKey(), stringObjectEntry.getValue());
}
return newSession;
}
P.S. I'm newbie in TCL.
please let me know if you need any further explanation.
There is a fix in OpenACS 5.9 that addresses your scanning reports. Please see the following discussion on OpenACS.org for reference.
http://www.openacs.org/forums/message-view?message_id=5332821
The problem that the OWASP report is talking about is the inability to migrate a session to use a new ID, making it easier for an attacker to discover the ID and reuse it. The protection against this is to change the session ID from time to time (no, I don't know how often!) and that Java code is involved in doing just that.
A session is represented as a token stored in the browser, usually in a cookie (and this is what cookies are designed to do). That token is then used to look up the database record corresponding to the session, which holds serializations of the key/value mappings in the session. It's a simple mechanism, but very powerful. The Java code for doing all this will be fairly complex behind the scenes because of the serialization, etc., but Tcl values are (usually, and always for built-in types) naturally serializable and so should prove much less of a problem in this; copying a session to a new key could be done without having to deserialize in the first place.
The exact code for doing this depends on the framework in use. I don't know what ]project-open[ uses, so that's as far as we can drill right now. You need to talk to other people actually working on PO…
For all that, the best way would be to make the key given to clients not be the primary key, so that you can change the session key without having to delete things. Just have a session key column (with an index!) and you'll be able to make things work fine. This is a more sophisticated approach though; it might not be practical to implement in your environment..

DevExpress XPO XPCollection Refreshing Changes

I'm experiencing an issue refreshing data in XPCollection after commiting some database changes using UnitOfWork.
I have a WinForm with an XPCollection.
The XPCollection uses XpoDefault.Session.
I do some changes through a UnitOfWork:
using (UnitOfWork uow = new UnitOfWork())
{
var photos = new XPCollection<Photo>(uow);
photos[0].Date = DateTime.Now;
uow.CommitTransaction();
}
To get the original XPCollection to update the changes, I've tried the following:
foreach (Photo photo in myXPCollection)
{
XpoDefault.Session.Reload(photo);
}
foreach (Photo photo in myXPCollection)
{
photo.Reload();
}
myXPCollection.Reload()
None of the methods work.The changes are not reflected in the original XPCollection.
They are only visible when I start with a completely new Session. Obviously, this is a big performance problem.
How to get the changes made using UnitOfWork to another Session?
You said:
They are only visible when I start with a completely new Session. Obviously, this is a big performance problem.
That's exactly what you should do. Create a new UnitOfWork each time you want refreshed data. A UnitOfWork is very cheap performance-wise to instantiate. If you have a large collection of Photo objects, you should improve performance by loading only the objects you need by specifying the Criteria parameter in the XPCollection<Photo> constructor.
When you issue Reload() it doesn't fetch anything from the database: it discards any changes and reloads the object from the session identity map. You can read this article and this Support Center issue for more information.
By the way, the DevExpress Support Center is by far the best place to ask DevExpress questions.

ASP.Net MVC 3: Where to handle session loss?

I've started bumping into errors when my session has been lost, or upon rebuilding my project, as my forms authentication cookie still lives on.
In WebForms I'd use the masterpage associated with pages which require login to simply check for the session.
How would I do this in one location in MVC ? I'd hate having to check for session state in every action in my controllers.
On the other hand I can't just apply a global filter either, since not all Controllers require session state.
Would it perhaps be possible in my layout view ? It's the only thing the pages which require session have in common.
One thing you could do is to sub-class the controllers that do need session state. This way you could create a filter on just this base controller. This would allow you to do it all in one place. Plus, as you pointed out, a global filter won't help you here since the logic does not apply to every controller.
add it to session start. if a session loss happens it needs to trigger a session start too. you can handle it in there as follows:
protected void Session_Start(object src, EventArgs e)
{
if (Context.Session != null)
{
if (Context.Session.IsNewSession)
{
string sCookieHeader = Request.Headers["Cookie"];
if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
{
// how to simulate it ???
// RedirectToAction(“ActionName”, “ControllerName”, route values);
Response.Redirect("/Home/TestAction");
}
}
}
}
I agree with what Steve has mentioned, but I suggest to use Global Filters instead of creating a base class for all your controllers. The reason for this is everytime you create a new controller, you should always remember to derive from the base controller or you may experience random behaviours in your application that may take you hours of debugging. This is especially important when you stop development for a while and then get back to it.
Also, another reason is the "Favour composition over inheritance" principle.

Resources