What's the correct way for shoppers (unregistered users) to access Product entity from portal module

Hi,

In addition to regular registered users, we have a portal module that allows many unregistered shoppers read the Product entity to be able to buy. We do not require shopper to register in order to shop.

  1. Is each shopper an anonymous login and the middleware creates an unique session for each shopper?

  2. Do all shoppers share the SAME login name and password specified in portal-app.properties:
    cuba.portal.anonymousUserLogin =xxx
    cuba.trustedClientPassword =yyy

In cuba-platform, can many shoppers co-exist in portal module? If yes, what is the correct way to organize many shoppers in terms of login, password, session parameters?

Thanks,
-Mike

Hi,

Portal uses single anonymous session for all the non-logged-in users. Thus, all anonymous users share the same login. Please note, that “cuba.trustedClientPassword” is not login of a user. It is a special token for portal application that enables portal to log in any user programmatically. It is a trusted token of an application itself.

Copy of anonymous session is stored in HTTP session of a portal user. It is created during creation of HTTP session.

If you set session attributes and locale to AnonymousSession of portal they will be store in HTTP session and will be passed to middleware for each request, so in middleware services you will be able to distinguish users by session parameters, but it will be possible only during a request.

1 Like

If you do not force users to log in / register you could use HTTP sessions to store additional information (for instance: Cart / goods) or session attributes. Please note that lifetime of HTTP session is limited by default, thus you have to configure session time-outs or use persistent HTTP sessions. Also, you can create an additional entity - PortalUserAccount and store it in the database, and add a cookie for a user with ID of this entity.

1 Like

That means only ONE anonymous user/shopper is able to shop at a time, which apparently does not work for a regular shopping case.

In order for multiple shoppers to shop simultaneously, can I pre-created let’s say 50 users pool, each user has its login credentials stored in database. When a new shopper requests to browse Product entity, the pool manager find the next available registered user from the pool, this shopper is then represented by the user from the pool until the shopping finishes and the pool is returned to the pool manager (similar to database connection pool concept). Do you think this will work? Any issues going this way?

Is there any example that demonstrates programmatically login using “cuba.trustedClientPassword”?

Thanks Yuriy,
-Mike

That means only ONE anonymous user/shopper is able to shop at a time, which apparently does not work for a regular shopping case.

No, as I said, you could use HTTP sessions to store additional information. HTTP session is created for each web browser that connects to a web application. There is no need for pool of users, because you can simply create an additional entity, e.g. SiteVisitor and store id of this entity in HTTP session or pass id of the entity to web browser as Cookie value.

Login using cuba.trustedClientPassword is easy:


@Inject
protected LoginService loginService;

@Inject
protected Configuration configuration;

...

PortalConfig portalConfig = configuration.getConfig(PortalConfig.class);
UserSession session;
try {
    session = loginService.loginTrusted("admin", // any login here
            portalConfig.getTrustedClientPassword(), Locale.ENGLISH);
} catch (LoginException e) {
    throw new RuntimeException("Unable to login");
}

// you can perform actions as user in obtained session
AppContext.withSecurityContext(new SecurityContext(session), () -> {
    // your code here
});

LoginService.loginTrusted - login on as any user using special secret system token called trustedClientPassword.

(Re-post with code syntax).

Hi Yuriy,

Your code above works great - the anonymous visitor is able to read/write database tables.

Now I am experiencing a little bit deeper problem under this same context: the anonymous session attribute variable can not be accessed from core function in:

<…>\modules\core\src\com\company\demo\orderlistener\OrderListener.java


public class OrderListener implements BeforeInsertEntityListener<Order> {

@Inject

private UserSessionSource userSessionSource;

@Override

public void onBeforeInsert(Order entity, EntityManager entityManager) {

String customerMobileNumber = userSessionSource.getUserSession().getAttribute("customerMobile");

/*

Either an authenticated login session or anonymous portal session has already

setup a session attribute "customerMobile". Thus entity.visitorMobile will get this

session attribute value as its value

*/

entity.setVisitorMobile(customerMobileNumber);

System.out.println("Session Attribute customerMobile: " + customerMobileNumber);

}

This OrderListener in core module set order’s visitorMobile property with the value get from the session attribute variable “customerMobile”. It works with the logged in userSession (with user name and password) , but does not work with the anonymous session (“Session Attribute customerMobile: null” is printed out).

Here is the code how I set the anonymous session attribute variable “customerMobile” (continued with your code in the previous post):


AppContext.withSecurityContext(new SecurityContext(session), () -> {

// your code here

session.setAttribute("customerMobile", "111-111-1111");

CommitContext commitContext = new CommitContext();

order = new Order();

if(order != null){

order.setTotal(BigDecimal.valueOf(20.66));

commitContext.addInstanceToCommit(order);

}

dataService.commit(commitContext);

});

I am hoping this order’s visitorMobile will be set to value “111-111-1111” in the OrderListener’s onBeforeInsert() method via session attribute variable “customerMobile”, but the value got from attribute variable “customerMobile” in onBeforeInsert() is null .

What is the correct way to pass the value from anonymous session so that the onBeforeInsert() method in core module is able to pick up?

Thanks,

-Mike

If you want to change session attribute you have to use UserSessionService setSessionAttribute method. Because userSession is not sent to server with each request, only session id is sent.

@Inject
protected LoginService loginService;
@Inject
protected UserSessionService userSessionService;
@Inject
protected Configuration configuration;

...

PortalConfig portalConfig = configuration.getConfig(PortalConfig.class);
UserSession session;
try {
    session = loginService.loginTrusted("admin", // any login here
            portalConfig.getTrustedClientPassword(), Locale.ENGLISH);
} catch (LoginException e) {
    throw new RuntimeException("Unable to login");
}

// you can perform actions as user in obtained session
AppContext.withSecurityContext(new SecurityContext(session), () -> {
    session.setAttribute("portal", "attribute-value");
    userSessionService.setSessionAttribute(session.getId(), "portal", "attribute-value");

    // your code here
});

In this case you will see attribute value inside of an EntityListener.

Hi Yuriy,

I tested. It works like a charm.

Thanks for your excellent support and help!
-Mike