SessionScoped Bean loses data on post-back on Google Appengine - google-app-engine

I use Eclipse 3.7 GAE pluggin for development. My application uses JSF and datastore, and was set up as per https://sites.google.com/a/wildstartech.com/adventures-in-java/Java-Platform-Enterprise-Edition/JavaServer-Faces/javaserver-faces-20/configuring-javaserver-faces-20-to-run-on-the-google-appengine. In my development system, it works well. But when deployed to GAE, the SessionScoped Bean loses data on post-back:
// Input facelet
<h:outputLabel for="popupCal">Date </h:outputLabel>
<p:calendar value="#{editEntry.current.date1}" id="popupCal" />
<h:outputLabel for="code">Code </h:outputLabel>
<h:inputText id="code" value="#{editEntry.current.accountCode}"/>
<h:outputLabel for="amt">Amount </h:outputLabel>
<h:inputText id="amt" value="#{editEntry.current.amountInDollars}"/>
<h:commandButton action="#{editEntry.createCashExpenditure}" value="Create Entry"/>
#ManagedBean(name="editEntry")
#SessionScoped
public class EditEntry extends AbstractEntryBean implements Serializable {
#ManagedProperty(value="#{sessionBean}")
protected SessionBean sessionBean;
#ManagedProperty(value="#{dao}")
protected Dao dao;
#PostConstruct
public void init() {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "dao is null? {0}", dao==null);
setTran_id(0L);
entries.clear();
setCurrent(new Entry());
getCurrent().clear();
...
this.refreshEntries();
}
public void refreshEntries() {
entries = dao.getEntries(current.getFinyr(), getTran_id());
Logger.getLogger(getClass().getName()).log(Level.INFO, "entries has {0} items", entries.size());
}
public String createCashExpenditure() {
if (dao == null) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "dao is null");
return null;
}
entries.clear();
Entry e = new Entry();
e.clear();
e.setAccountCode(current.getAccountCode());
e.setAccountName(dao.lookupAccoutName(e.getAccountCode()));
e.setAmount(current.getAmount());
e.setDate1(current.getDate1());
e.setTran_id(getTran_id());
Key key = dao.saveEntry(e, sessionBean.getFinyr());
e.setId(key.getId());
entries.add(e);
current = e;
this.setTran_id(e.getTran_id());
Logger.getLogger(getClass().getName()).log(Level.INFO, "current account is: {0}", current.getAccountCode());
return "newEntry?faces-redirect=true";
}
...
}
newEntry.xhtml
<p:dataTable id="items" value="#{editEntry.entries}" var="item">
// editEntry.entries is EMPTY!
When EditEntry.createCashExpenditure() is invoked, the log shows EditEntry.current is correctly populated, and saved to the datastore. Datastore viewer also displays the data. But on post-back, in newEntry.xhtml facelet, editEntry.entries becomes empty, EditEntry.current loses all data.
I have put in place ForceSessionSerializationPhaseListener as mentioned in http://java.zacheusz.eu/google-app-engine-http-session-vs-jsf-en/394/ The log shows this listener is invoked.
In web.xml, javax.faces.PROJECT_STAGE is Production,

I face the same issues, after redirect, previous session is gone. It only happen when deploy online.
I think that is due to session variable set to 'client' for javax.faces.STATE_SAVING_METHOD (in web.xml)
so before redirect, I need explicit set the session as below:
getSessionScope().put(sessionname,sessionObj);
public Map getSessionScope() {
return getFacesContext().getExternalContext().getSessionMap();
}

Related

Blazor Wasm setting end user defined startpage

In my application I have many areas for different kinds of users. Controlled by roles.
So I would like to give the user an option to set a preferred startpage.
Deep Linking works, so no problems there.
My first attempt was this
#code{
[Parameter] public string Action { get; set; }
[Inject] private NavigationManager Navigation { get; set; }
private void LoginSucceeded(RemoteAuthenticationState state)
{
Console.WriteLine("navigate to DepartmentAccess");
// This works with on extra login
Navigation.NavigateTo("/DepartmentAccess", true);
// This loads 3 or more times
// state.ReturnUrl = "/DepartmentAccess";
}
}
Then I tried altering the return url in RedirectToLogin.razor. Here I added “MyStartPage”
#inject NavigationManager Navigation
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
#using Zeus.Client.PortfolioRights
#code {
protected override void OnInitialized()
{
Navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)/MyStartPage}");
}
}
This has absolutely no effekt!
Okay time to dig a little deeper
It’s the RemoteAuthenticatorViewCore that holds all the code related to the login process.
This function handles the Login state. It’s called after the redirect from Azure AD
private async Task ProcessLogIn(string returnUrl)
{
AuthenticationState.ReturnUrl = returnUrl;
var result = await AuthenticationService.SignInAsync(new RemoteAuthenticationContext<TAuthenticationState>
{
State = AuthenticationState
});
switch (result.Status)
{
case RemoteAuthenticationStatus.Redirect:
break;
case RemoteAuthenticationStatus.Success:
await OnLogInSucceeded.InvokeAsync(result.State);
await NavigateToReturnUrl(GetReturnUrl(result.State, returnUrl));
break;
case RemoteAuthenticationStatus.Failure:
_message = result.ErrorMessage;
Navigation.NavigateTo(ApplicationPaths.LogInFailedPath);
break;
case RemoteAuthenticationStatus.OperationCompleted:
default:
throw new InvalidOperationException($"Invalid authentication result status '{result.Status}'.");
}
}
The input parameter “returnUri” are set from this function
private string GetReturnUrl(TAuthenticationState state, string defaultReturnUrl = null)
{
if (state?.ReturnUrl != null)
{
return state.ReturnUrl;
}
var fromQuery = QueryStringHelper.GetParameter(new Uri(Navigation.Uri).Query, "returnUrl");
if (!string.IsNullOrWhiteSpace(fromQuery) && !fromQuery.StartsWith(Navigation.BaseUri))
{
// This is an extra check to prevent open redirects.
throw new InvalidOperationException("Invalid return url. The return url needs to have the same origin as the current page.");
}
return fromQuery ?? defaultReturnUrl ?? Navigation.BaseUri;
}
So I wonder. Why is the “defaultReturnUrl” not set when I alter the return uri?
Navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)/MyStartPage}
I guess that I don't understand the login flow. But got a feeling that I am close.
Just need to find a way to set the defaultReturnUrl
Please check if below points can be worked around.
The first step in getting a page (component) ready to participate in routing is to assign the component a route. You do that in the cshmtl file, using the page directive.
#page "/redirectpage" above #inject NavigationManager NavManager
Make sure to Add CascadingAuthenticationState and <AuthorizeView> in apps.razor and LoginDisplay component.It is responsible to display pages that the user is authorised to see.
(or)
If the page component for the route contains an authorize attribute (#attribute [Authorize]) then the user must be logged in, otherwise they will be redirected to the login page.
Example: if you wanted to secure the Counter.razor page just add an Authorize attribute to the top:
#using Microsoft.AspNetCore.Authorization
#attribute [Authorize]
Or
#attribute [Authorize(Roles = "finance")]
Add this to the pages that require authentication.
References:
Secure an ASP.NET Core Blazor WebAssembly standalone app with the
Authentication library | Microsoft Docs
Using Azure Active Directory to Secure Blazor WebAssembly Hosted
Apps (code-maze.com)
Secure an ASP.NET Core Blazor WebAssembly standalone app with Azure
Active Directory | Microsoft Docs

Ho do i pass a model with data from the DB to an ABP.IO Layout Hook?

trying to setup a multi-tenant site using ABP.io framework 3.1.
I am trying to set the <meta keywords (amongst other tags) in the page html head. I am attempting to get the values from a database field for the current tenant so the meta keywords will be specific for the tenant.
I tried to follow the sample that is available here: https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface#layout-hooks where they inject a google analytics script code into the head tag.
this is fine, as it is static text, but when i try to load the partial page with a model it throws an error of expecting a different model to that which is passed in.
So far i have the Notification View Componet
Public class MetaKeywordViewComponent : AbpViewComponent
{
public async Task<IViewComponentResult> InvokeAsync() {
return View("/Pages/Shared/Components/Head/MetaKeyword.cshtml"); //, meta);
}
}
and the cshtml page
#using MyCompany.MyProduct.Web.Pages.Shared.Components.Head
#model MetaKeywordModel
#if (Model.SiteData.Keywords.Length > 0)
{
<meta content="#Model.SiteData.Keywords" name="keywords" />
}
and the cshtml.cs file as
public class MetaKeywordModel : MyProductPageModel
{
private readonly ITenantSiteDataAppService _tenantSiteDataAppService;
public TenantSiteDataDto SiteData { get; private set; }
public MetaKeywordModel(ITenantSiteDataAppService tenantSiteDataAppService)
{
_tenantSiteDataAppService = tenantSiteDataAppService;
}
public virtual async Task<ActionResult> OnGetAsync()
{
if (CurrentTenant != null)
{
SiteData = await _tenantSiteDataAppService.GetSiteDataAsync();
}
return Page();
}
}
but when i run the program i get the following error.
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook.LayoutHookViewModel', but this ViewDataDictionary instance requires a model item of type 'MyCompany.MyProduct.TenantData.Dtos.TenantSiteDataDto'.
How do i pass the data from my database into the page to be rendered if i can't use my model?
Any help tips or tricks would be greatly appreciated.
Regards
Matty
ViewComponent is different from the razor page.
See https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-3.1#view-components
You should inject the service in view component class directly. like:
public class MetaKeywordViewComponent : AbpViewComponent
{
private readonly ITenantSiteDataAppService _tenantSiteDataAppService;
public MetaKeywordViewComponent(ITenantSiteDataAppService tenantSiteDataAppService)
{
_tenantSiteDataAppService = tenantSiteDataAppService;
}
public async Task<IViewComponentResult> InvokeAsync()
{
return View("/Pages/Shared/Components/Head/MetaKeyword.cshtml",
await _tenantSiteDataAppService.GetSiteDataAsync());
}
}
In addition, you can refer https://github.com/abpframework/abp/blob/42f37c5ff01ad853a5425d15539d4222cd0dab69/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/PageAlerts/PageAlertsViewComponent.cs

ASP.NET MVC | ActionResult not getting called when going back to previous page

I understand that the title of the question may be vague but then that's the best way I could come up with to explain my issue at hand.
I'm overriding the OnActionExecuting function to manage my session related activities and allow/ deny requests to authorized/ unauthorized users, respectively. Along with tracking of the session, I'm also using the OnActionExecuting to load user available features for the current page into a temporary class and accessing from the view using ajax call.
namespace MyApp.Controllers
{
public class TESTController : Controller
{
[SessionTimeout]
public ActionResult Index()
{
return this.View();
}
}
}
public class SessionTimeoutAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
if (ctx.Session["AppUser"] == null)
{
// Redirect to the login page
// Or deny request
}
else
{
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var actionName = filterContext.ActionDescriptor.ActionName;
var methodType = ((ReflectedActionDescriptor)filterContext.ActionDescriptor).MethodInfo.ReturnType;
if (methodType == typeof(ActionResult))
{
// Load all user access rights for the current page into a temporary memory
// by using the Action and Controller name
}
}
base.OnActionExecuting(filterContext);
}
}
The above works like a charm.. But the issue is when the user clicks on the back button of the browser or hits the backspace key. In that case, the OnActionExecuting function is never called for the ActionResult and further I am unable to load the current page access rights for the user.
Thanks & Regards,
Kshitij
Adding the following to my ActionResult made the above code to work.
[SessionTimeout]
[OutputCache(Duration = 0, NoStore = true)]
public ActionResult SomeView()
{
return this.View();
}

Redirect if claim is missing in RequiresAuthentication

I'm working on setting up two-factor in my application and I'm trying to make it redirect back to the verification page if the user is logged in but not verified (I'm keeping track of if the user is verified in the sessions table which is added to ClaimsPrincipal 'IsVerified').
The problem i'm having is the example I am using from the documentation doesn't seem to be working properly:
public static class ModuleSecurity
{
public static string[] ExcludedPaths = { "/", "/login", "/login/verify", "/admin/settings", "/login/tf/setup" };
public static void RequiresAuthentication(this NancyModule module)
{
module.Before.AddItemToEndOfPipeline(RequiresAuthentication);
}
private static Response RequiresAuthentication(NancyContext context)
{
// Check if user is authenticated
if (context.CurrentUser == null)
return new Response() { StatusCode = HttpStatusCode.Unauthorized };
if (context.CurrentUser.FindFirst("RequireVerification")?.Value == "True" && context.CurrentUser.FindFirst("IsVerified")?.Value != "True" && !ExcludedPaths.Any(x => x.ToLower() == context.Request.Path.ToLower()))
return new Response().WithHeader("Location", "/login/verify").WithContentType("text/html").WithStatusCode(HttpStatusCode.SeeOther);
return null;
}
}
Putting break points in I see the "module.Before.AddItemToEndOfPipeline" is executed but it is not executing the other method I have.
Then problem was I was adding this to the BEFORE pipeline but i'm calling this.RequiresClaims after the route was triggered (so I needed the AFTER pipeline). I was able to do this by adding the extensions and using the module.AddBeforeOrExecute option.

How to configure Spring and Angular to work together

I have a spring REST server (v3.2) and AngularJS for the client code.
From my understanding in the basic scenario the user navigates to the base domain .com, index.html is being sent back and
and from that point Angular manages the communication.
My questions are:
1. How to set Spring to return the Angular file.
2. How to handle a situation where the user does not go though the base domain and just navigates to
.com/books/moby-dick which currently returns a JSON representation of the Moby-Dick book that was suppose
to be rendered by the client
A good tutorial will be highly appreciated.
This is my web initialzer class:
public class WebAppInitializer implements WebApplicationInitializer {
private static Logger LOG = LoggerFactory.getLogger(WebAppInitializer.class);
#Override
public void onStartup(ServletContext servletContext) {
WebApplicationContext rootContext = createRootContext(servletContext);
configureSpringMvc(servletContext, rootContext);
FilterRegistration.Dynamic corsFilter = servletContext.addFilter("corsFilter", CORSFilter.class);
corsFilter.addMappingForUrlPatterns(null, false, "/*");
// configureSpringSecurity(servletContext, rootContext);
}
private WebApplicationContext createRootContext(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
// rootContext.register(CoreConfig.class, SecurityConfig.class);
rootContext.register(CoreConfig.class);
servletContext.addListener(new ContextLoaderListener(rootContext));
servletContext.setInitParameter("defaultHtmlEscape", "true");
return rootContext;
}
private void configureSpringMvc(ServletContext servletContext, WebApplicationContext rootContext) {
AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
mvcContext.register(MVCConfig.class);
mvcContext.setParent(rootContext);
ServletRegistration.Dynamic appServlet = servletContext.addServlet(
"webservice", new DispatcherServlet(mvcContext));
appServlet.setLoadOnStartup(1);
Set<String> mappingConflicts = appServlet.addMapping("/");
if (!mappingConflicts.isEmpty()) {
for (String s : mappingConflicts) {
LOG.error("Mapping conflict: " + s);
}
throw new IllegalStateException(
"'webservice' cannot be mapped to '/'");
}
}
This is my MVC configuration file:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.yadazing.rest.controller"})
public class MVCConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
(disclaimer: I am the author of JHipster)
You can have a look at JHipster which will generate such an application for you, with a Spring backend and an AngularJS frontend.
As the generator goes far beyond what you need (security, etc), you can also have a look at our sample application.
How about this then for #1:
registry.addResourceHandler("/index.html").addResourceLocations("/index.html");

Resources